summaryrefslogtreecommitdiffstats
path: root/external_libs/python/python-daemon-2.0.5
diff options
context:
space:
mode:
Diffstat (limited to 'external_libs/python/python-daemon-2.0.5')
-rw-r--r--external_libs/python/python-daemon-2.0.5/ChangeLog380
-rw-r--r--external_libs/python/python-daemon-2.0.5/LICENSE.ASF-2202
-rw-r--r--external_libs/python/python-daemon-2.0.5/LICENSE.GPL-3674
-rw-r--r--external_libs/python/python-daemon-2.0.5/MANIFEST.in7
-rw-r--r--external_libs/python/python-daemon-2.0.5/PKG-INFO38
-rw-r--r--external_libs/python/python-daemon-2.0.5/daemon/__init__.py49
-rw-r--r--external_libs/python/python-daemon-2.0.5/daemon/_metadata.py152
-rw-r--r--external_libs/python/python-daemon-2.0.5/daemon/daemon.py926
-rw-r--r--external_libs/python/python-daemon-2.0.5/daemon/pidfile.py67
-rw-r--r--external_libs/python/python-daemon-2.0.5/daemon/runner.py324
-rw-r--r--external_libs/python/python-daemon-2.0.5/doc/CREDITS53
-rw-r--r--external_libs/python/python-daemon-2.0.5/doc/FAQ156
-rw-r--r--external_libs/python/python-daemon-2.0.5/doc/TODO95
-rw-r--r--external_libs/python/python-daemon-2.0.5/doc/hacking.txt180
-rw-r--r--external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/PKG-INFO38
-rw-r--r--external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/SOURCES.txt30
-rw-r--r--external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/dependency_links.txt1
-rw-r--r--external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/not-zip-safe1
-rw-r--r--external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/requires.txt3
-rw-r--r--external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/top_level.txt1
-rw-r--r--external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/version_info.json6
-rw-r--r--external_libs/python/python-daemon-2.0.5/setup.cfg11
-rw-r--r--external_libs/python/python-daemon-2.0.5/setup.py106
-rw-r--r--external_libs/python/python-daemon-2.0.5/test/__init__.py23
-rw-r--r--external_libs/python/python-daemon-2.0.5/test/scaffold.py322
-rw-r--r--external_libs/python/python-daemon-2.0.5/test/test_daemon.py1744
-rw-r--r--external_libs/python/python-daemon-2.0.5/test/test_metadata.py380
-rw-r--r--external_libs/python/python-daemon-2.0.5/test/test_pidfile.py472
-rw-r--r--external_libs/python/python-daemon-2.0.5/test/test_runner.py675
-rw-r--r--external_libs/python/python-daemon-2.0.5/test_version.py1373
-rw-r--r--external_libs/python/python-daemon-2.0.5/version.py547
31 files changed, 0 insertions, 9036 deletions
diff --git a/external_libs/python/python-daemon-2.0.5/ChangeLog b/external_libs/python/python-daemon-2.0.5/ChangeLog
deleted file mode 100644
index 4975f781..00000000
--- a/external_libs/python/python-daemon-2.0.5/ChangeLog
+++ /dev/null
@@ -1,380 +0,0 @@
-Version 2.0.5
-=============
-
-:Released: 2015-02-02
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Refine compatibility of exceptions for file operations.
-* Specify the text encoding when opening the changelog file.
-
-
-Version 2.0.4
-=============
-
-:Released: 2015-01-23
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Record version info via Setuptools commands.
-* Remove the custom Setuptools entry points.
- This closes Alioth bug#314948.
-
-
-Version 2.0.3
-=============
-
-:Released: 2015-01-14
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Break circular import dependency for ‘setup.py’.
-* Refactor all initial metadata functionality to ‘daemon._metadata’.
-* Distribute ‘version’ (and its tests) only in source, not install.
-* Build a “universal” (Python 2 and Python 3) wheel.
-
-
-Version 2.0.2
-=============
-
-:Released: 2015-01-13
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Declare test-time dependency on recent ‘unittest2’.
-* Declare packaging-time dependency on ‘docutils’ library.
-* Include unit tests for ‘version’ module with source distribution.
-* Record version info consistent with distribution metadata.
-
-
-Version 2.0.1
-=============
-
-:Released: 2015-01-11
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Include the ‘version’ module with source distribution.
-
-
-Version 2.0
-===========
-
-:Released: 2015-01-10
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Support both Python 3 (version 3.2 or later) and Python 2 (version
- 2.7 or later).
-* Document the API of all functions comprehensively in docstrings.
-* Add a hacking guide for developers.
-* Add explicit credit for contributors.
-* Document the security impact of the default umask.
-
-* Specify explicit text or binary mode when opening files.
-* Preserve exception context in custom exceptions.
-
-* Declare compatibility with current Python versions.
-* Depend on Python 3 compatible libraries.
-* Update package homepage to Alioth hosted project page.
-* Use ‘pydoc.splitdoc’ to get package description text.
-* Remove ASCII translation of package description, not needed now the
- docstring is a proper Unicode text value.
-* Include test suite with source distribution.
-* Move package metadata to ‘daemon/_metadata.py’.
-* Migrate to JSON (instead of Python) for serialised version info.
-* Add unit tests for metadata.
-* Store and retrieve version info in Setuptools metadata.
-
-* Migrate to ‘str.format’ for interpolation of values into text.
-* Migrate to ‘mock’ library for mock objects in tests.
-* Migrate to ‘testscenarios’ library for unit test scenarios.
-* Migrate to ‘unittest2’ library for back-ported improvements.
- Remove custom test suite creation.
-* Discriminate Python 2-and-3 compatible usage of dict methods.
-* Discriminate Python 2-and-3 compatible bytes versus text.
-* Declare explicit absolute and relative imports.
-* Discriminate between different ‘fileno’ method behaviours.
- In Python 3, ‘StringIO.fileno’ is callable but raises an exception.
-* Migrate to built-in ‘next’ function.
-* Wrap the ‘fromlist’ parameter of ‘__import__’ for Python 3
- compatibility.
-* Wrap function introspection for Python 3 compatibility.
-* Wrap standard library imports where names changed in Python 3.
-
-
-Version 1.6.1
-=============
-
-:Released: 2014-08-04
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Use unambiguous “except FooType as foo” syntax.
- This is to ease the port to Python 3, where the ambiguous comma
- usage is an error.
-* Ensure a ‘basestring’ name bound to the base type for strings.
- This is to allow checks to work on Python 2 and 3.
-* Specify versions of Python supported, as trove classifiers.
-
-* Update copyright notices.
-* Add editor hints for most files.
-* Distinguish continuation-line indentation versus block indentation.
-
-* Use unicode literals by default, specifying bytes where necessary.
- This is to ease the port to Python 3, where the default string type
- is unicode.
-* Update copyright notices.
-* Update the GPL license file to version 3, as declared in our
- copyright notices.
-
-* Change license of library code to Apache License 2.0. Rationale at
- <URL:http://wiki.python.org/moin/PythonSoftwareFoundationLicenseFaq#Contributing_Code_to_Python>.
-
-
-Version 1.6
-===========
-
-:Released: 2010-05-10
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Use absolute imports to disambiguate provenance of names.
-* setup.py: Require ‘lockfile >=0.9’.
-* daemon/pidfile.py: Renamed from ‘daemon/pidlockfile.py’. Change
- references elsewhere to use this new name.
-* test/test_pidfile.py: Renamed from ‘test/test_pidlockfile.py’.
- Change references elsewhere to use this new name.
-* daemon/pidfile.py: Remove functionality now migrated to ‘lockfile’
- library.
-
-* FAQ: Add some entries and re-structure the document.
-
-* Use ‘unicode’ data type for all text values.
-* Prepare for Python 3 upgrade by tweaking some names and imports.
-
-* MANIFEST.in: Include the documentation in the distribution.
-
-
-Version 1.5.5
-=============
-
-:Released: 2010-03-02
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Stop using ‘pkg_resources’ and revert to pre-1.5.3 version-string
- handling, until a better way that doesn't break everyone else's
- installation can be found.
-
-
-Version 1.5.4
-=============
-
-:Released: 2010-02-27
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* MANIFEST.in: Explicitly include version data file, otherwise
- everything breaks for users of the sdist.
-
-
-Version 1.5.3
-=============
-
-:Released: 2010-02-26
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* daemon/daemon.py: Invoke the pidfile context manager's ‘__exit__’
- method with the correct arguments (as per
- <URL:http://docs.python.org/library/stdtypes.html#typecontextmanager>).
- Thanks to Ludvig Ericson for the bug report.
-* version: New plain-text data file to store project version string.
-* setup.py: Read version string from data file.
-* daemon/version/__init__.py: Query version string with ‘pkg_resources’.
-
-* Add ‘pylint’ configuration for this project.
-* Update copyright notices.
-
-
-Version 1.5.2
-=============
-
-:Released: 2009-10-24
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Ensure we only prevent core dumps if ‘prevent_core’ is true.
- Thanks to Denis Bilenko for reporting the lacking implementation of
- this documented option.
-
-* Add initial Frequently Asked Questions document.
-
-
-Version 1.5.1
-=============
-
-:Released: 2009-09-26
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Make a separate collection of DaemonRunner test scenarios.
-* Handle a start request with a timeout on the PID file lock acquire.
-
-* Implement ‘TimeoutPIDLockFile’ to specify a timeout in advance of
- lock acquisition.
-* Use lock with timeout for ‘DaemonRunner’.
-
-
-Version 1.5
-===========
-
-:Released: 2009-09-24
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Make a separate collection of PIDLockFile test scenarios.
-
-* Raise specific errors on ‘DaemonRunner’ failures.
-* Distinguish different conditions on reading and parsing PID file.
-* Refactor code to ‘_terminate_daemon_process’ method.
-* Improve explanations in comments and docstrings.
-* Don't set pidfile at all if no path specified to constructor.
-* Write the PID file using correct OS locking and permissions.
-* Close the PID file after writing.
-* Implement ‘PIDLockFile’ as subclass of ‘lockfile.LinkFileLock’.
-* Remove redundant checks for file existence.
-
-* Manage the excluded file descriptors as a set (not a list).
-* Only inspect the file descriptor of streams if they actually have
- one (via a ‘fileno’ method) when determining which file descriptors
- to close. Thanks to Ask Solem for revealing this bug.
-
-
-Version 1.4.8
-=============
-
-:Released: 2009-09-17
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Remove child-exit signal (‘SIGCLD’, ‘SIGCHLD’) from default signal
- map. Thanks to Joel Martin for pinpointing this issue.
-* Document requirement for ensuring any operating-system specific
- signal handlers are considered.
-* Refactor ‘fork_then_exit_parent’ functionality to avoid duplicate
- code.
-* Remove redundant imports.
-* Remove unused code from unit test suite scaffold.
-* Add specific license terms for unit test suite scaffold.
-
-
-Version 1.4.7
-=============
-
-:Released: 2009-09-03
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Fix keywords argument for distribution setup.
-* Exclude ‘test’ package from distribution installation.
-
-
-Version 1.4.6
-=============
-
-:Released: 2009-06-21
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Update documentation for changes from latest PEP 3143 revision.
-* Implement DaemonContext.is_open method.
-
-
-Version 1.4.5
-=============
-
-:Released: 2009-05-17
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Register DaemonContext.close method for atexit processing.
-* Move PID file cleanup to close method.
-* Improve docstrings by reference to, and copy from, PEP 3143.
-* Use mock checking capabilities of newer ‘MiniMock’ library.
-* Automate building a versioned distribution tarball.
-* Include developer documentation files in source distribution.
-
-
-Version 1.4.4
-=============
-
-:Released: 2009-03-26
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Conform to current PEP version, now released as PEP 3143 “Standard
- daemon process library”.
-* Ensure UID and GID are set in correct order.
-* Delay closing all open files until just before re-binding standard
- streams.
-* Redirect standard streams to null device by default.
-
-
-Version 1.4.3
-=============
-
-:Released: 2009-03-19
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Close the PID file context on exit.
-
-
-Version 1.4.2
-=============
-
-:Released: 2009-03-18
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Context manager methods for DaemonContext.
-
-
-Version 1.4.1
-=============
-
-:Released: 2009-03-18
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Improvements to docstrings.
-* Further conformance with draft PEP.
-
-
-Version 1.4
-===========
-
-:Released: 2009-03-17
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Implement the interface from a draft PEP for process daemonisation.
-* Complete statement coverage from unit test suite.
-
-
-Version 1.3
-===========
-
-:Released: 2009-03-12
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Separate controller (now ‘DaemonRunner’) from daemon process
- context (now ‘DaemonContext’).
-* Fix many corner cases and bugs.
-* Huge increase in unit test suite.
-
-
-Version 1.2
-===========
-
-:Released: 2009-01-27
-:Maintainer: Ben Finney <ben+python@benfinney.id.au>
-
-* Initial release of this project forked from ‘bda.daemon’. Thanks,
- Robert Niederreiter.
-* Refactor some functionality out to helper functions.
-* Begin unit test suite.
-
-
-..
- This is free software: you may copy, modify, and/or distribute this work
- under the terms of the Apache License version 2.0 as published by the
- Apache Software Foundation.
- No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-..
- Local variables:
- coding: utf-8
- mode: text
- mode: rst
- End:
- vim: fileencoding=utf-8 filetype=rst :
diff --git a/external_libs/python/python-daemon-2.0.5/LICENSE.ASF-2 b/external_libs/python/python-daemon-2.0.5/LICENSE.ASF-2
deleted file mode 100644
index d6456956..00000000
--- a/external_libs/python/python-daemon-2.0.5/LICENSE.ASF-2
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/external_libs/python/python-daemon-2.0.5/LICENSE.GPL-3 b/external_libs/python/python-daemon-2.0.5/LICENSE.GPL-3
deleted file mode 100644
index 94a9ed02..00000000
--- a/external_libs/python/python-daemon-2.0.5/LICENSE.GPL-3
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- <program> Copyright (C) <year> <name of author>
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-<http://www.gnu.org/licenses/>.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/external_libs/python/python-daemon-2.0.5/MANIFEST.in b/external_libs/python/python-daemon-2.0.5/MANIFEST.in
deleted file mode 100644
index d3d4341e..00000000
--- a/external_libs/python/python-daemon-2.0.5/MANIFEST.in
+++ /dev/null
@@ -1,7 +0,0 @@
-include MANIFEST.in
-include LICENSE.*
-include ChangeLog
-recursive-include doc *
-include version.py
-include test_version.py
-recursive-include test *.py
diff --git a/external_libs/python/python-daemon-2.0.5/PKG-INFO b/external_libs/python/python-daemon-2.0.5/PKG-INFO
deleted file mode 100644
index fd81f509..00000000
--- a/external_libs/python/python-daemon-2.0.5/PKG-INFO
+++ /dev/null
@@ -1,38 +0,0 @@
-Metadata-Version: 1.1
-Name: python-daemon
-Version: 2.0.5
-Summary: Library to implement a well-behaved Unix daemon process.
-Home-page: https://alioth.debian.org/projects/python-daemon/
-Author: Ben Finney
-Author-email: ben+python@benfinney.id.au
-License: Apache-2
-Description: This library implements the well-behaved daemon specification of
- :pep:`3143`, “Standard daemon process library”.
-
- A well-behaved Unix daemon process is tricky to get right, but the
- required steps are much the same for every daemon program. A
- `DaemonContext` instance holds the behaviour and configured
- process environment for the program; use the instance as a context
- manager to enter a daemon state.
-
- Simple example of usage::
-
- import daemon
-
- from spam import do_main_program
-
- with daemon.DaemonContext():
- do_main_program()
-
- Customisation of the steps to become a daemon is available by
- setting options on the `DaemonContext` instance; see the
- documentation for that class for each option.
-Keywords: daemon,fork,unix
-Platform: UNKNOWN
-Classifier: Development Status :: 4 - Beta
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Operating System :: POSIX
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Intended Audience :: Developers
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/external_libs/python/python-daemon-2.0.5/daemon/__init__.py b/external_libs/python/python-daemon-2.0.5/daemon/__init__.py
deleted file mode 100644
index 4731a6ef..00000000
--- a/external_libs/python/python-daemon-2.0.5/daemon/__init__.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# daemon/__init__.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2009–2015 Ben Finney <ben+python@benfinney.id.au>
-# Copyright © 2006 Robert Niederreiter
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the Apache License, version 2.0 as published by the
-# Apache Software Foundation.
-# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-""" Library to implement a well-behaved Unix daemon process.
-
- This library implements the well-behaved daemon specification of
- :pep:`3143`, “Standard daemon process library”.
-
- A well-behaved Unix daemon process is tricky to get right, but the
- required steps are much the same for every daemon program. A
- `DaemonContext` instance holds the behaviour and configured
- process environment for the program; use the instance as a context
- manager to enter a daemon state.
-
- Simple example of usage::
-
- import daemon
-
- from spam import do_main_program
-
- with daemon.DaemonContext():
- do_main_program()
-
- Customisation of the steps to become a daemon is available by
- setting options on the `DaemonContext` instance; see the
- documentation for that class for each option.
-
- """
-
-from __future__ import (absolute_import, unicode_literals)
-
-from .daemon import DaemonContext
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/external_libs/python/python-daemon-2.0.5/daemon/_metadata.py b/external_libs/python/python-daemon-2.0.5/daemon/_metadata.py
deleted file mode 100644
index 6d22a2b7..00000000
--- a/external_libs/python/python-daemon-2.0.5/daemon/_metadata.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# daemon/_metadata.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the Apache License, version 2.0 as published by the
-# Apache Software Foundation.
-# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-""" Package metadata for the ‘python-daemon’ distribution. """
-
-from __future__ import (absolute_import, unicode_literals)
-
-import json
-import re
-import collections
-import datetime
-
-import pkg_resources
-
-
-distribution_name = "python-daemon"
-version_info_filename = "version_info.json"
-
-def get_distribution_version_info(filename=version_info_filename):
- """ Get the version info from the installed distribution.
-
- :param filename: Base filename of the version info resource.
- :return: The version info as a mapping of fields. If the
- distribution is not available, the mapping is empty.
-
- The version info is stored as a metadata file in the
- distribution.
-
- """
- version_info = {
- 'release_date': "UNKNOWN",
- 'version': "UNKNOWN",
- 'maintainer': "UNKNOWN",
- }
-
- try:
- distribution = pkg_resources.get_distribution(distribution_name)
- except pkg_resources.DistributionNotFound:
- distribution = None
-
- if distribution is not None:
- if distribution.has_metadata(version_info_filename):
- content = distribution.get_metadata(version_info_filename)
- version_info = json.loads(content)
-
- return version_info
-
-version_info = get_distribution_version_info()
-
-version_installed = version_info['version']
-
-
-rfc822_person_regex = re.compile(
- "^(?P<name>[^<]+) <(?P<email>[^>]+)>$")
-
-ParsedPerson = collections.namedtuple('ParsedPerson', ['name', 'email'])
-
-def parse_person_field(value):
- """ Parse a person field into name and email address.
-
- :param value: The text value specifying a person.
- :return: A 2-tuple (name, email) for the person's details.
-
- If the `value` does not match a standard person with email
- address, the `email` item is ``None``.
-
- """
- result = (None, None)
-
- match = rfc822_person_regex.match(value)
- if len(value):
- if match is not None:
- result = ParsedPerson(
- name=match.group('name'),
- email=match.group('email'))
- else:
- result = ParsedPerson(name=value, email=None)
-
- return result
-
-author_name = "Ben Finney"
-author_email = "ben+python@benfinney.id.au"
-author = "{name} <{email}>".format(name=author_name, email=author_email)
-
-
-class YearRange:
- """ A range of years spanning a period. """
-
- def __init__(self, begin, end=None):
- self.begin = begin
- self.end = end
-
- def __unicode__(self):
- text = "{range.begin:04d}".format(range=self)
- if self.end is not None:
- if self.end > self.begin:
- text = "{range.begin:04d}–{range.end:04d}".format(range=self)
- return text
-
- __str__ = __unicode__
-
-
-def make_year_range(begin_year, end_date=None):
- """ Construct the year range given a start and possible end date.
-
- :param begin_date: The beginning year (text) for the range.
- :param end_date: The end date (text, ISO-8601 format) for the
- range, or a non-date token string.
- :return: The range of years as a `YearRange` instance.
-
- If the `end_date` is not a valid ISO-8601 date string, the
- range has ``None`` for the end year.
-
- """
- begin_year = int(begin_year)
-
- try:
- end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d")
- except (TypeError, ValueError):
- # Specified end_date value is not a valid date.
- end_year = None
- else:
- end_year = end_date.year
-
- year_range = YearRange(begin=begin_year, end=end_year)
-
- return year_range
-
-copyright_year_begin = "2001"
-build_date = version_info['release_date']
-copyright_year_range = make_year_range(copyright_year_begin, build_date)
-
-copyright = "Copyright © {year_range} {author} and others".format(
- year_range=copyright_year_range, author=author)
-license = "Apache-2"
-url = "https://alioth.debian.org/projects/python-daemon/"
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/external_libs/python/python-daemon-2.0.5/daemon/daemon.py b/external_libs/python/python-daemon-2.0.5/daemon/daemon.py
deleted file mode 100644
index 07810cf1..00000000
--- a/external_libs/python/python-daemon-2.0.5/daemon/daemon.py
+++ /dev/null
@@ -1,926 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# daemon/daemon.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
-# Copyright © 2007–2008 Robert Niederreiter, Jens Klein
-# Copyright © 2004–2005 Chad J. Schroeder
-# Copyright © 2003 Clark Evans
-# Copyright © 2002 Noah Spurrier
-# Copyright © 2001 Jürgen Hermann
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the Apache License, version 2.0 as published by the
-# Apache Software Foundation.
-# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-""" Daemon process behaviour.
- """
-
-from __future__ import (absolute_import, unicode_literals)
-
-import os
-import sys
-import resource
-import errno
-import signal
-import socket
-import atexit
-try:
- # Python 2 has both ‘str’ (bytes) and ‘unicode’ (text).
- basestring = basestring
- unicode = unicode
-except NameError:
- # Python 3 names the Unicode data type ‘str’.
- basestring = str
- unicode = str
-
-
-class DaemonError(Exception):
- """ Base exception class for errors from this module. """
-
- def __init__(self, *args, **kwargs):
- self._chain_from_context()
-
- super(DaemonError, self).__init__(*args, **kwargs)
-
- def _chain_from_context(self):
- _chain_exception_from_existing_exception_context(self, as_cause=True)
-
-
-class DaemonOSEnvironmentError(DaemonError, OSError):
- """ Exception raised when daemon OS environment setup receives error. """
-
-
-class DaemonProcessDetachError(DaemonError, OSError):
- """ Exception raised when process detach fails. """
-
-
-class DaemonContext:
- """ Context for turning the current program into a daemon process.
-
- A `DaemonContext` instance represents the behaviour settings and
- process context for the program when it becomes a daemon. The
- behaviour and environment is customised by setting options on the
- instance, before calling the `open` method.
-
- Each option can be passed as a keyword argument to the `DaemonContext`
- constructor, or subsequently altered by assigning to an attribute on
- the instance at any time prior to calling `open`. That is, for
- options named `wibble` and `wubble`, the following invocation::
-
- foo = daemon.DaemonContext(wibble=bar, wubble=baz)
- foo.open()
-
- is equivalent to::
-
- foo = daemon.DaemonContext()
- foo.wibble = bar
- foo.wubble = baz
- foo.open()
-
- The following options are defined.
-
- `files_preserve`
- :Default: ``None``
-
- List of files that should *not* be closed when starting the
- daemon. If ``None``, all open file descriptors will be closed.
-
- Elements of the list are file descriptors (as returned by a file
- object's `fileno()` method) or Python `file` objects. Each
- specifies a file that is not to be closed during daemon start.
-
- `chroot_directory`
- :Default: ``None``
-
- Full path to a directory to set as the effective root directory of
- the process. If ``None``, specifies that the root directory is not
- to be changed.
-
- `working_directory`
- :Default: ``'/'``
-
- Full path of the working directory to which the process should
- change on daemon start.
-
- Since a filesystem cannot be unmounted if a process has its
- current working directory on that filesystem, this should either
- be left at default or set to a directory that is a sensible “home
- directory” for the daemon while it is running.
-
- `umask`
- :Default: ``0``
-
- File access creation mask (“umask”) to set for the process on
- daemon start.
-
- A daemon should not rely on the parent process's umask value,
- which is beyond its control and may prevent creating a file with
- the required access mode. So when the daemon context opens, the
- umask is set to an explicit known value.
-
- If the conventional value of 0 is too open, consider setting a
- value such as 0o022, 0o027, 0o077, or another specific value.
- Otherwise, ensure the daemon creates every file with an
- explicit access mode for the purpose.
-
- `pidfile`
- :Default: ``None``
-
- Context manager for a PID lock file. When the daemon context opens
- and closes, it enters and exits the `pidfile` context manager.
-
- `detach_process`
- :Default: ``None``
-
- If ``True``, detach the process context when opening the daemon
- context; if ``False``, do not detach.
-
- If unspecified (``None``) during initialisation of the instance,
- this will be set to ``True`` by default, and ``False`` only if
- detaching the process is determined to be redundant; for example,
- in the case when the process was started by `init`, by `initd`, or
- by `inetd`.
-
- `signal_map`
- :Default: system-dependent
-
- Mapping from operating system signals to callback actions.
-
- The mapping is used when the daemon context opens, and determines
- the action for each signal's signal handler:
-
- * A value of ``None`` will ignore the signal (by setting the
- signal action to ``signal.SIG_IGN``).
-
- * A string value will be used as the name of an attribute on the
- ``DaemonContext`` instance. The attribute's value will be used
- as the action for the signal handler.
-
- * Any other value will be used as the action for the
- signal handler. See the ``signal.signal`` documentation
- for details of the signal handler interface.
-
- The default value depends on which signals are defined on the
- running system. Each item from the list below whose signal is
- actually defined in the ``signal`` module will appear in the
- default map:
-
- * ``signal.SIGTTIN``: ``None``
-
- * ``signal.SIGTTOU``: ``None``
-
- * ``signal.SIGTSTP``: ``None``
-
- * ``signal.SIGTERM``: ``'terminate'``
-
- Depending on how the program will interact with its child
- processes, it may need to specify a signal map that
- includes the ``signal.SIGCHLD`` signal (received when a
- child process exits). See the specific operating system's
- documentation for more detail on how to determine what
- circumstances dictate the need for signal handlers.
-
- `uid`
- :Default: ``os.getuid()``
-
- `gid`
- :Default: ``os.getgid()``
-
- The user ID (“UID”) value and group ID (“GID”) value to switch
- the process to on daemon start.
-
- The default values, the real UID and GID of the process, will
- relinquish any effective privilege elevation inherited by the
- process.
-
- `prevent_core`
- :Default: ``True``
-
- If true, prevents the generation of core files, in order to avoid
- leaking sensitive information from daemons run as `root`.
-
- `stdin`
- :Default: ``None``
-
- `stdout`
- :Default: ``None``
-
- `stderr`
- :Default: ``None``
-
- Each of `stdin`, `stdout`, and `stderr` is a file-like object
- which will be used as the new file for the standard I/O stream
- `sys.stdin`, `sys.stdout`, and `sys.stderr` respectively. The file
- should therefore be open, with a minimum of mode 'r' in the case
- of `stdin`, and mimimum of mode 'w+' in the case of `stdout` and
- `stderr`.
-
- If the object has a `fileno()` method that returns a file
- descriptor, the corresponding file will be excluded from being
- closed during daemon start (that is, it will be treated as though
- it were listed in `files_preserve`).
-
- If ``None``, the corresponding system stream is re-bound to the
- file named by `os.devnull`.
-
- """
-
- __metaclass__ = type
-
- def __init__(
- self,
- chroot_directory=None,
- working_directory="/",
- umask=0,
- uid=None,
- gid=None,
- prevent_core=True,
- detach_process=None,
- files_preserve=None,
- pidfile=None,
- stdin=None,
- stdout=None,
- stderr=None,
- signal_map=None,
- ):
- """ Set up a new instance. """
- self.chroot_directory = chroot_directory
- self.working_directory = working_directory
- self.umask = umask
- self.prevent_core = prevent_core
- self.files_preserve = files_preserve
- self.pidfile = pidfile
- self.stdin = stdin
- self.stdout = stdout
- self.stderr = stderr
-
- if uid is None:
- uid = os.getuid()
- self.uid = uid
- if gid is None:
- gid = os.getgid()
- self.gid = gid
-
- if detach_process is None:
- detach_process = is_detach_process_context_required()
- self.detach_process = detach_process
-
- if signal_map is None:
- signal_map = make_default_signal_map()
- self.signal_map = signal_map
-
- self._is_open = False
-
- @property
- def is_open(self):
- """ ``True`` if the instance is currently open. """
- return self._is_open
-
- def open(self):
- """ Become a daemon process.
-
- :return: ``None``.
-
- Open the daemon context, turning the current program into a daemon
- process. This performs the following steps:
-
- * If this instance's `is_open` property is true, return
- immediately. This makes it safe to call `open` multiple times on
- an instance.
-
- * If the `prevent_core` attribute is true, set the resource limits
- for the process to prevent any core dump from the process.
-
- * If the `chroot_directory` attribute is not ``None``, set the
- effective root directory of the process to that directory (via
- `os.chroot`).
-
- This allows running the daemon process inside a “chroot gaol”
- as a means of limiting the system's exposure to rogue behaviour
- by the process. Note that the specified directory needs to
- already be set up for this purpose.
-
- * Set the process UID and GID to the `uid` and `gid` attribute
- values.
-
- * Close all open file descriptors. This excludes those listed in
- the `files_preserve` attribute, and those that correspond to the
- `stdin`, `stdout`, or `stderr` attributes.
-
- * Change current working directory to the path specified by the
- `working_directory` attribute.
-
- * Reset the file access creation mask to the value specified by
- the `umask` attribute.
-
- * If the `detach_process` option is true, detach the current
- process into its own process group, and disassociate from any
- controlling terminal.
-
- * Set signal handlers as specified by the `signal_map` attribute.
-
- * If any of the attributes `stdin`, `stdout`, `stderr` are not
- ``None``, bind the system streams `sys.stdin`, `sys.stdout`,
- and/or `sys.stderr` to the files represented by the
- corresponding attributes. Where the attribute has a file
- descriptor, the descriptor is duplicated (instead of re-binding
- the name).
-
- * If the `pidfile` attribute is not ``None``, enter its context
- manager.
-
- * Mark this instance as open (for the purpose of future `open` and
- `close` calls).
-
- * Register the `close` method to be called during Python's exit
- processing.
-
- When the function returns, the running program is a daemon
- process.
-
- """
- if self.is_open:
- return
-
- if self.chroot_directory is not None:
- change_root_directory(self.chroot_directory)
-
- if self.prevent_core:
- prevent_core_dump()
-
- change_file_creation_mask(self.umask)
- change_working_directory(self.working_directory)
- change_process_owner(self.uid, self.gid)
-
- if self.detach_process:
- detach_process_context()
-
- signal_handler_map = self._make_signal_handler_map()
- set_signal_handlers(signal_handler_map)
-
- exclude_fds = self._get_exclude_file_descriptors()
- close_all_open_files(exclude=exclude_fds)
-
- redirect_stream(sys.stdin, self.stdin)
- redirect_stream(sys.stdout, self.stdout)
- redirect_stream(sys.stderr, self.stderr)
-
- if self.pidfile is not None:
- self.pidfile.__enter__()
-
- self._is_open = True
-
- register_atexit_function(self.close)
-
- def __enter__(self):
- """ Context manager entry point. """
- self.open()
- return self
-
- def close(self):
- """ Exit the daemon process context.
-
- :return: ``None``.
-
- Close the daemon context. This performs the following steps:
-
- * If this instance's `is_open` property is false, return
- immediately. This makes it safe to call `close` multiple times
- on an instance.
-
- * If the `pidfile` attribute is not ``None``, exit its context
- manager.
-
- * Mark this instance as closed (for the purpose of future `open`
- and `close` calls).
-
- """
- if not self.is_open:
- return
-
- if self.pidfile is not None:
- # Follow the interface for telling a context manager to exit,
- # <URL:http://docs.python.org/library/stdtypes.html#typecontextmanager>.
- self.pidfile.__exit__(None, None, None)
-
- self._is_open = False
-
- def __exit__(self, exc_type, exc_value, traceback):
- """ Context manager exit point. """
- self.close()
-
- def terminate(self, signal_number, stack_frame):
- """ Signal handler for end-process signals.
-
- :param signal_number: The OS signal number received.
- :param stack_frame: The frame object at the point the
- signal was received.
- :return: ``None``.
-
- Signal handler for the ``signal.SIGTERM`` signal. Performs the
- following step:
-
- * Raise a ``SystemExit`` exception explaining the signal.
-
- """
- exception = SystemExit(
- "Terminating on signal {signal_number!r}".format(
- signal_number=signal_number))
- raise exception
-
- def _get_exclude_file_descriptors(self):
- """ Get the set of file descriptors to exclude closing.
-
- :return: A set containing the file descriptors for the
- files to be preserved.
-
- The file descriptors to be preserved are those from the
- items in `files_preserve`, and also each of `stdin`,
- `stdout`, and `stderr`. For each item:
-
- * If the item is ``None``, it is omitted from the return
- set.
-
- * If the item's ``fileno()`` method returns a value, that
- value is in the return set.
-
- * Otherwise, the item is in the return set verbatim.
-
- """
- files_preserve = self.files_preserve
- if files_preserve is None:
- files_preserve = []
- files_preserve.extend(
- item for item in [self.stdin, self.stdout, self.stderr]
- if hasattr(item, 'fileno'))
-
- exclude_descriptors = set()
- for item in files_preserve:
- if item is None:
- continue
- file_descriptor = _get_file_descriptor(item)
- if file_descriptor is not None:
- exclude_descriptors.add(file_descriptor)
- else:
- exclude_descriptors.add(item)
-
- return exclude_descriptors
-
- def _make_signal_handler(self, target):
- """ Make the signal handler for a specified target object.
-
- :param target: A specification of the target for the
- handler; see below.
- :return: The value for use by `signal.signal()`.
-
- If `target` is ``None``, return ``signal.SIG_IGN``. If `target`
- is a text string, return the attribute of this instance named
- by that string. Otherwise, return `target` itself.
-
- """
- if target is None:
- result = signal.SIG_IGN
- elif isinstance(target, unicode):
- name = target
- result = getattr(self, name)
- else:
- result = target
-
- return result
-
- def _make_signal_handler_map(self):
- """ Make the map from signals to handlers for this instance.
-
- :return: The constructed signal map for this instance.
-
- Construct a map from signal numbers to handlers for this
- context instance, suitable for passing to
- `set_signal_handlers`.
-
- """
- signal_handler_map = dict(
- (signal_number, self._make_signal_handler(target))
- for (signal_number, target) in self.signal_map.items())
- return signal_handler_map
-
-
-def _get_file_descriptor(obj):
- """ Get the file descriptor, if the object has one.
-
- :param obj: The object expected to be a file-like object.
- :return: The file descriptor iff the file supports it; otherwise
- ``None``.
-
- The object may be a non-file object. It may also be a
- file-like object with no support for a file descriptor. In
- either case, return ``None``.
-
- """
- file_descriptor = None
- if hasattr(obj, 'fileno'):
- try:
- file_descriptor = obj.fileno()
- except ValueError:
- # The item doesn't support a file descriptor.
- pass
-
- return file_descriptor
-
-
-def change_working_directory(directory):
- """ Change the working directory of this process.
-
- :param directory: The target directory path.
- :return: ``None``.
-
- """
- try:
- os.chdir(directory)
- except Exception as exc:
- error = DaemonOSEnvironmentError(
- "Unable to change working directory ({exc})".format(exc=exc))
- raise error
-
-
-def change_root_directory(directory):
- """ Change the root directory of this process.
-
- :param directory: The target directory path.
- :return: ``None``.
-
- Set the current working directory, then the process root directory,
- to the specified `directory`. Requires appropriate OS privileges
- for this process.
-
- """
- try:
- os.chdir(directory)
- os.chroot(directory)
- except Exception as exc:
- error = DaemonOSEnvironmentError(
- "Unable to change root directory ({exc})".format(exc=exc))
- raise error
-
-
-def change_file_creation_mask(mask):
- """ Change the file creation mask for this process.
-
- :param mask: The numeric file creation mask to set.
- :return: ``None``.
-
- """
- try:
- os.umask(mask)
- except Exception as exc:
- error = DaemonOSEnvironmentError(
- "Unable to change file creation mask ({exc})".format(exc=exc))
- raise error
-
-
-def change_process_owner(uid, gid):
- """ Change the owning UID and GID of this process.
-
- :param uid: The target UID for the daemon process.
- :param gid: The target GID for the daemon process.
- :return: ``None``.
-
- Set the GID then the UID of the process (in that order, to avoid
- permission errors) to the specified `gid` and `uid` values.
- Requires appropriate OS privileges for this process.
-
- """
- try:
- os.setgid(gid)
- os.setuid(uid)
- except Exception as exc:
- error = DaemonOSEnvironmentError(
- "Unable to change process owner ({exc})".format(exc=exc))
- raise error
-
-
-def prevent_core_dump():
- """ Prevent this process from generating a core dump.
-
- :return: ``None``.
-
- Set the soft and hard limits for core dump size to zero. On Unix,
- this entirely prevents the process from creating core dump.
-
- """
- core_resource = resource.RLIMIT_CORE
-
- try:
- # Ensure the resource limit exists on this platform, by requesting
- # its current value.
- core_limit_prev = resource.getrlimit(core_resource)
- except ValueError as exc:
- error = DaemonOSEnvironmentError(
- "System does not support RLIMIT_CORE resource limit"
- " ({exc})".format(exc=exc))
- raise error
-
- # Set hard and soft limits to zero, i.e. no core dump at all.
- core_limit = (0, 0)
- resource.setrlimit(core_resource, core_limit)
-
-
-def detach_process_context():
- """ Detach the process context from parent and session.
-
- :return: ``None``.
-
- Detach from the parent process and session group, allowing the
- parent to exit while this process continues running.
-
- Reference: “Advanced Programming in the Unix Environment”,
- section 13.3, by W. Richard Stevens, published 1993 by
- Addison-Wesley.
-
- """
-
- def fork_then_exit_parent(error_message):
- """ Fork a child process, then exit the parent process.
-
- :param error_message: Message for the exception in case of a
- detach failure.
- :return: ``None``.
- :raise DaemonProcessDetachError: If the fork fails.
-
- """
- try:
- pid = os.fork()
- if pid > 0:
- os._exit(0)
- except OSError as exc:
- error = DaemonProcessDetachError(
- "{message}: [{exc.errno:d}] {exc.strerror}".format(
- message=error_message, exc=exc))
- raise error
-
- fork_then_exit_parent(error_message="Failed first fork")
- os.setsid()
- fork_then_exit_parent(error_message="Failed second fork")
-
-
-def is_process_started_by_init():
- """ Determine whether the current process is started by `init`.
-
- :return: ``True`` iff the parent process is `init`; otherwise
- ``False``.
-
- The `init` process is the one with process ID of 1.
-
- """
- result = False
-
- init_pid = 1
- if os.getppid() == init_pid:
- result = True
-
- return result
-
-
-def is_socket(fd):
- """ Determine whether the file descriptor is a socket.
-
- :param fd: The file descriptor to interrogate.
- :return: ``True`` iff the file descriptor is a socket; otherwise
- ``False``.
-
- Query the socket type of `fd`. If there is no error, the file is a
- socket.
-
- """
- result = False
-
- file_socket = socket.fromfd(fd, socket.AF_INET, socket.SOCK_RAW)
-
- try:
- socket_type = file_socket.getsockopt(
- socket.SOL_SOCKET, socket.SO_TYPE)
- except socket.error as exc:
- exc_errno = exc.args[0]
- if exc_errno == errno.ENOTSOCK:
- # Socket operation on non-socket.
- pass
- else:
- # Some other socket error.
- result = True
- else:
- # No error getting socket type.
- result = True
-
- return result
-
-
-def is_process_started_by_superserver():
- """ Determine whether the current process is started by the superserver.
-
- :return: ``True`` if this process was started by the internet
- superserver; otherwise ``False``.
-
- The internet superserver creates a network socket, and
- attaches it to the standard streams of the child process. If
- that is the case for this process, return ``True``, otherwise
- ``False``.
-
- """
- result = False
-
- stdin_fd = sys.__stdin__.fileno()
- if is_socket(stdin_fd):
- result = True
-
- return result
-
-
-def is_detach_process_context_required():
- """ Determine whether detaching the process context is required.
-
- :return: ``True`` iff the process is already detached; otherwise
- ``False``.
-
- The process environment is interrogated for the following:
-
- * Process was started by `init`; or
-
- * Process was started by `inetd`.
-
- If any of the above are true, the process is deemed to be already
- detached.
-
- """
- result = True
- if is_process_started_by_init() or is_process_started_by_superserver():
- result = False
-
- return result
-
-
-def close_file_descriptor_if_open(fd):
- """ Close a file descriptor if already open.
-
- :param fd: The file descriptor to close.
- :return: ``None``.
-
- Close the file descriptor `fd`, suppressing an error in the
- case the file was not open.
-
- """
- try:
- os.close(fd)
- except EnvironmentError as exc:
- if exc.errno == errno.EBADF:
- # File descriptor was not open.
- pass
- else:
- error = DaemonOSEnvironmentError(
- "Failed to close file descriptor {fd:d} ({exc})".format(
- fd=fd, exc=exc))
- raise error
-
-
-MAXFD = 2048
-
-def get_maximum_file_descriptors():
- """ Get the maximum number of open file descriptors for this process.
-
- :return: The number (integer) to use as the maximum number of open
- files for this process.
-
- The maximum is the process hard resource limit of maximum number of
- open file descriptors. If the limit is “infinity”, a default value
- of ``MAXFD`` is returned.
-
- """
- limits = resource.getrlimit(resource.RLIMIT_NOFILE)
- result = limits[1]
- if result == resource.RLIM_INFINITY:
- result = MAXFD
- return result
-
-
-def close_all_open_files(exclude=set()):
- """ Close all open file descriptors.
-
- :param exclude: Collection of file descriptors to skip when closing
- files.
- :return: ``None``.
-
- Closes every file descriptor (if open) of this process. If
- specified, `exclude` is a set of file descriptors to *not*
- close.
-
- """
- maxfd = get_maximum_file_descriptors()
- for fd in reversed(range(maxfd)):
- if fd not in exclude:
- close_file_descriptor_if_open(fd)
-
-
-def redirect_stream(system_stream, target_stream):
- """ Redirect a system stream to a specified file.
-
- :param standard_stream: A file object representing a standard I/O
- stream.
- :param target_stream: The target file object for the redirected
- stream, or ``None`` to specify the null device.
- :return: ``None``.
-
- `system_stream` is a standard system stream such as
- ``sys.stdout``. `target_stream` is an open file object that
- should replace the corresponding system stream object.
-
- If `target_stream` is ``None``, defaults to opening the
- operating system's null device and using its file descriptor.
-
- """
- if target_stream is None:
- target_fd = os.open(os.devnull, os.O_RDWR)
- else:
- target_fd = target_stream.fileno()
- os.dup2(target_fd, system_stream.fileno())
-
-
-def make_default_signal_map():
- """ Make the default signal map for this system.
-
- :return: A mapping from signal number to handler object.
-
- The signals available differ by system. The map will not contain
- any signals not defined on the running system.
-
- """
- name_map = {
- 'SIGTSTP': None,
- 'SIGTTIN': None,
- 'SIGTTOU': None,
- 'SIGTERM': 'terminate',
- }
- signal_map = dict(
- (getattr(signal, name), target)
- for (name, target) in name_map.items()
- if hasattr(signal, name))
-
- return signal_map
-
-
-def set_signal_handlers(signal_handler_map):
- """ Set the signal handlers as specified.
-
- :param signal_handler_map: A map from signal number to handler
- object.
- :return: ``None``.
-
- See the `signal` module for details on signal numbers and signal
- handlers.
-
- """
- for (signal_number, handler) in signal_handler_map.items():
- signal.signal(signal_number, handler)
-
-
-def register_atexit_function(func):
- """ Register a function for processing at program exit.
-
- :param func: A callable function expecting no arguments.
- :return: ``None``.
-
- The function `func` is registered for a call with no arguments
- at program exit.
-
- """
- atexit.register(func)
-
-
-def _chain_exception_from_existing_exception_context(exc, as_cause=False):
- """ Decorate the specified exception with the existing exception context.
-
- :param exc: The exception instance to decorate.
- :param as_cause: If true, the existing context is declared to be
- the cause of the exception.
- :return: ``None``.
-
- :PEP:`344` describes syntax and attributes (`__traceback__`,
- `__context__`, `__cause__`) for use in exception chaining.
-
- Python 2 does not have that syntax, so this function decorates
- the exception with values from the current exception context.
-
- """
- (existing_exc_type, existing_exc, existing_traceback) = sys.exc_info()
- if as_cause:
- exc.__cause__ = existing_exc
- else:
- exc.__context__ = existing_exc
- exc.__traceback__ = existing_traceback
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/external_libs/python/python-daemon-2.0.5/daemon/pidfile.py b/external_libs/python/python-daemon-2.0.5/daemon/pidfile.py
deleted file mode 100644
index 4517ee0e..00000000
--- a/external_libs/python/python-daemon-2.0.5/daemon/pidfile.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# daemon/pidfile.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the Apache License, version 2.0 as published by the
-# Apache Software Foundation.
-# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-""" Lockfile behaviour implemented via Unix PID files.
- """
-
-from __future__ import (absolute_import, unicode_literals)
-
-from lockfile.pidlockfile import PIDLockFile
-
-
-class TimeoutPIDLockFile(PIDLockFile, object):
- """ Lockfile with default timeout, implemented as a Unix PID file.
-
- This uses the ``PIDLockFile`` implementation, with the
- following changes:
-
- * The `acquire_timeout` parameter to the initialiser will be
- used as the default `timeout` parameter for the `acquire`
- method.
-
- """
-
- def __init__(self, path, acquire_timeout=None, *args, **kwargs):
- """ Set up the parameters of a TimeoutPIDLockFile.
-
- :param path: Filesystem path to the PID file.
- :param acquire_timeout: Value to use by default for the
- `acquire` call.
- :return: ``None``.
-
- """
- self.acquire_timeout = acquire_timeout
- super(TimeoutPIDLockFile, self).__init__(path, *args, **kwargs)
-
- def acquire(self, timeout=None, *args, **kwargs):
- """ Acquire the lock.
-
- :param timeout: Specifies the timeout; see below for valid
- values.
- :return: ``None``.
-
- The `timeout` defaults to the value set during
- initialisation with the `acquire_timeout` parameter. It is
- passed to `PIDLockFile.acquire`; see that method for
- details.
-
- """
- if timeout is None:
- timeout = self.acquire_timeout
- super(TimeoutPIDLockFile, self).acquire(timeout, *args, **kwargs)
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/external_libs/python/python-daemon-2.0.5/daemon/runner.py b/external_libs/python/python-daemon-2.0.5/daemon/runner.py
deleted file mode 100644
index 6973cf1c..00000000
--- a/external_libs/python/python-daemon-2.0.5/daemon/runner.py
+++ /dev/null
@@ -1,324 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# daemon/runner.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2009–2015 Ben Finney <ben+python@benfinney.id.au>
-# Copyright © 2007–2008 Robert Niederreiter, Jens Klein
-# Copyright © 2003 Clark Evans
-# Copyright © 2002 Noah Spurrier
-# Copyright © 2001 Jürgen Hermann
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the Apache License, version 2.0 as published by the
-# Apache Software Foundation.
-# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-""" Daemon runner library.
- """
-
-from __future__ import (absolute_import, unicode_literals)
-
-import sys
-import os
-import signal
-import errno
-try:
- # Python 3 standard library.
- ProcessLookupError
-except NameError:
- # No such class in Python 2.
- ProcessLookupError = NotImplemented
-
-import lockfile
-
-from . import pidfile
-from .daemon import (basestring, unicode)
-from .daemon import DaemonContext
-from .daemon import _chain_exception_from_existing_exception_context
-
-
-class DaemonRunnerError(Exception):
- """ Abstract base class for errors from DaemonRunner. """
-
- def __init__(self, *args, **kwargs):
- self._chain_from_context()
-
- super(DaemonRunnerError, self).__init__(*args, **kwargs)
-
- def _chain_from_context(self):
- _chain_exception_from_existing_exception_context(self, as_cause=True)
-
-
-class DaemonRunnerInvalidActionError(DaemonRunnerError, ValueError):
- """ Raised when specified action for DaemonRunner is invalid. """
-
- def _chain_from_context(self):
- # This exception is normally not caused by another.
- _chain_exception_from_existing_exception_context(self, as_cause=False)
-
-
-class DaemonRunnerStartFailureError(DaemonRunnerError, RuntimeError):
- """ Raised when failure starting DaemonRunner. """
-
-
-class DaemonRunnerStopFailureError(DaemonRunnerError, RuntimeError):
- """ Raised when failure stopping DaemonRunner. """
-
-
-class DaemonRunner:
- """ Controller for a callable running in a separate background process.
-
- The first command-line argument is the action to take:
-
- * 'start': Become a daemon and call `app.run()`.
- * 'stop': Exit the daemon process specified in the PID file.
- * 'restart': Stop, then start.
-
- """
-
- __metaclass__ = type
-
- start_message = "started with pid {pid:d}"
-
- def __init__(self, app):
- """ Set up the parameters of a new runner.
-
- :param app: The application instance; see below.
- :return: ``None``.
-
- The `app` argument must have the following attributes:
-
- * `stdin_path`, `stdout_path`, `stderr_path`: Filesystem paths
- to open and replace the existing `sys.stdin`, `sys.stdout`,
- `sys.stderr`.
-
- * `pidfile_path`: Absolute filesystem path to a file that will
- be used as the PID file for the daemon. If ``None``, no PID
- file will be used.
-
- * `pidfile_timeout`: Used as the default acquisition timeout
- value supplied to the runner's PID lock file.
-
- * `run`: Callable that will be invoked when the daemon is
- started.
-
- """
- self.parse_args()
- self.app = app
- self.daemon_context = DaemonContext()
- self.daemon_context.stdin = open(app.stdin_path, 'rt')
- self.daemon_context.stdout = open(app.stdout_path, 'w+t')
- self.daemon_context.stderr = open(
- app.stderr_path, 'w+t', buffering=0)
-
- self.pidfile = None
- if app.pidfile_path is not None:
- self.pidfile = make_pidlockfile(
- app.pidfile_path, app.pidfile_timeout)
- self.daemon_context.pidfile = self.pidfile
-
- def _usage_exit(self, argv):
- """ Emit a usage message, then exit.
-
- :param argv: The command-line arguments used to invoke the
- program, as a sequence of strings.
- :return: ``None``.
-
- """
- progname = os.path.basename(argv[0])
- usage_exit_code = 2
- action_usage = "|".join(self.action_funcs.keys())
- message = "usage: {progname} {usage}".format(
- progname=progname, usage=action_usage)
- emit_message(message)
- sys.exit(usage_exit_code)
-
- def parse_args(self, argv=None):
- """ Parse command-line arguments.
-
- :param argv: The command-line arguments used to invoke the
- program, as a sequence of strings.
-
- :return: ``None``.
-
- The parser expects the first argument as the program name, the
- second argument as the action to perform.
-
- If the parser fails to parse the arguments, emit a usage
- message and exit the program.
-
- """
- if argv is None:
- argv = sys.argv
-
- min_args = 2
- if len(argv) < min_args:
- self._usage_exit(argv)
-
- self.action = unicode(argv[1])
- if self.action not in self.action_funcs:
- self._usage_exit(argv)
-
- def _start(self):
- """ Open the daemon context and run the application.
-
- :return: ``None``.
- :raises DaemonRunnerStartFailureError: If the PID file cannot
- be locked by this process.
-
- """
- if is_pidfile_stale(self.pidfile):
- self.pidfile.break_lock()
-
- try:
- self.daemon_context.open()
- except lockfile.AlreadyLocked:
- error = DaemonRunnerStartFailureError(
- "PID file {pidfile.path!r} already locked".format(
- pidfile=self.pidfile))
- raise error
-
- pid = os.getpid()
- message = self.start_message.format(pid=pid)
- emit_message(message)
-
- self.app.run()
-
- def _terminate_daemon_process(self):
- """ Terminate the daemon process specified in the current PID file.
-
- :return: ``None``.
- :raises DaemonRunnerStopFailureError: If terminating the daemon
- fails with an OS error.
-
- """
- pid = self.pidfile.read_pid()
- try:
- os.kill(pid, signal.SIGTERM)
- except OSError as exc:
- error = DaemonRunnerStopFailureError(
- "Failed to terminate {pid:d}: {exc}".format(
- pid=pid, exc=exc))
- raise error
-
- def _stop(self):
- """ Exit the daemon process specified in the current PID file.
-
- :return: ``None``.
- :raises DaemonRunnerStopFailureError: If the PID file is not
- already locked.
-
- """
- if not self.pidfile.is_locked():
- error = DaemonRunnerStopFailureError(
- "PID file {pidfile.path!r} not locked".format(
- pidfile=self.pidfile))
- raise error
-
- if is_pidfile_stale(self.pidfile):
- self.pidfile.break_lock()
- else:
- self._terminate_daemon_process()
-
- def _restart(self):
- """ Stop, then start.
- """
- self._stop()
- self._start()
-
- action_funcs = {
- 'start': _start,
- 'stop': _stop,
- 'restart': _restart,
- }
-
- def _get_action_func(self):
- """ Get the function for the specified action.
-
- :return: The function object corresponding to the specified
- action.
- :raises DaemonRunnerInvalidActionError: if the action is
- unknown.
-
- The action is specified by the `action` attribute, which is set
- during `parse_args`.
-
- """
- try:
- func = self.action_funcs[self.action]
- except KeyError:
- error = DaemonRunnerInvalidActionError(
- "Unknown action: {action!r}".format(
- action=self.action))
- raise error
- return func
-
- def do_action(self):
- """ Perform the requested action.
-
- :return: ``None``.
-
- The action is specified by the `action` attribute, which is set
- during `parse_args`.
-
- """
- func = self._get_action_func()
- func(self)
-
-
-def emit_message(message, stream=None):
- """ Emit a message to the specified stream (default `sys.stderr`). """
- if stream is None:
- stream = sys.stderr
- stream.write("{message}\n".format(message=message))
- stream.flush()
-
-
-def make_pidlockfile(path, acquire_timeout):
- """ Make a PIDLockFile instance with the given filesystem path. """
- if not isinstance(path, basestring):
- error = ValueError("Not a filesystem path: {path!r}".format(
- path=path))
- raise error
- if not os.path.isabs(path):
- error = ValueError("Not an absolute path: {path!r}".format(
- path=path))
- raise error
- lockfile = pidfile.TimeoutPIDLockFile(path, acquire_timeout)
-
- return lockfile
-
-
-def is_pidfile_stale(pidfile):
- """ Determine whether a PID file is stale.
-
- :return: ``True`` iff the PID file is stale; otherwise ``False``.
-
- The PID file is “stale” if its contents are valid but do not
- match the PID of a currently-running process.
-
- """
- result = False
-
- pidfile_pid = pidfile.read_pid()
- if pidfile_pid is not None:
- try:
- os.kill(pidfile_pid, signal.SIG_DFL)
- except ProcessLookupError:
- # The specified PID does not exist.
- result = True
- except OSError as exc:
- if exc.errno == errno.ESRCH:
- # Under Python 2, process lookup error is an OSError.
- # The specified PID does not exist.
- result = True
-
- return result
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/external_libs/python/python-daemon-2.0.5/doc/CREDITS b/external_libs/python/python-daemon-2.0.5/doc/CREDITS
deleted file mode 100644
index feb65d5e..00000000
--- a/external_libs/python/python-daemon-2.0.5/doc/CREDITS
+++ /dev/null
@@ -1,53 +0,0 @@
-Credits for contributors to ‘python-daemon’
-###########################################
-
-:Updated: 2014-12-23
-
-The ‘python-daemon’ library is the work of many contributors.
-
-
-Primary developers
-==================
-
-The library has been maintained over the years by:
-
-* Ben Finney <ben+python@benfinney.id.au>
-* Robert Niederreiter
-* Jens Klein
-
-
-Precursors
-==========
-
-The library code base is inherited from prior work by:
-
-* Chad J. Schroeder
-* Clark Evans
-* Noah Spurrier
-* Jürgen Hermann
-
-
-Additional contributors
-=======================
-
-People who have also contributed substantial improvements:
-
-
-
-..
- This is free software: you may copy, modify, and/or distribute this work
- under the terms of the Apache License version 2.0 as published by the
- Apache Software Foundation.
- No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-..
- Local variables:
- coding: utf-8
- mode: text
- mode: rst
- time-stamp-format: "%:y-%02m-%02d"
- time-stamp-start: "^:Updated:[ ]+"
- time-stamp-end: "$"
- time-stamp-line-limit: 20
- End:
- vim: fileencoding=utf-8 filetype=rst :
diff --git a/external_libs/python/python-daemon-2.0.5/doc/FAQ b/external_libs/python/python-daemon-2.0.5/doc/FAQ
deleted file mode 100644
index 1fcc4658..00000000
--- a/external_libs/python/python-daemon-2.0.5/doc/FAQ
+++ /dev/null
@@ -1,156 +0,0 @@
-‘python-daemon’ Frequently Asked Questions
-##########################################
-
-:Author: Ben Finney <ben+python@benfinney.id.au>
-:Updated: 2015-01-10
-
-.. contents::
-..
- 1 General
- 1.1 What is the purpose of the ‘python-daemon’ library?
- 1.2 How can I run a service communicating with a separate daemon process?
- 2 Security
- 2.1 Why is the umask set to 0 by default?
- 3 File descriptors
- 3.1 Why does the output stop after opening the daemon context?
- 3.2 How can I preserve a ‘logging’ handler's file descriptor?
-
-General
-=======
-
-What is the purpose of the ‘python-daemon’ library?
----------------------------------------------------
-
-The ‘python-daemon’ library has a deliberately narrow focus: that of
-being a reference implementation for `PEP 3143`_, “Standard daemon
-process library”.
-
-.. _`PEP 3143`: http://www.python.org/dev/peps/pep-3143
-
-How can I run a service communicating with a separate daemon process?
----------------------------------------------------------------------
-
-As specified in `PEP 3143`_, the ‘python-daemon’ library is
-specifically focussed on the goal of having the *current running
-program* become a well-behaved Unix daemon process. This leaves open
-the question of how this program is started, or about multiple
-programs interacting. As detailed in PEP 3143:
-
- A daemon is not a service
-
- There is a related concept in many systems, called a “service”. A
- service differs from the model in this PEP, in that rather than
- having the *current* program continue to run as a daemon process,
- a service starts an *additional* process to run in the background,
- and the current process communicates with that additional process
- via some defined channels.
-
- The Unix-style daemon model in this PEP can be used, among other
- things, to implement the background-process part of a service; but
- this PEP does not address the other aspects of setting up and
- managing a service.
-
-A possible starting point for such a “service” model of execution is
-in a `message from 2009-01-30`_ to the ``python-ideas`` forum.
-
-.. _`message from 2009-01-30`: http://mail.python.org/pipermail/python-ideas/2009-January/002606.html
-
-
-Security
-========
-
-Why is the umask set to 0 by default?
--------------------------------------
-
-A daemon should not rely on the parent process's umask value, which is
-beyond its control and may prevent creating a file with the required
-access mode. So when the daemon context opens, the umask is set to an
-explicit known value.
-
-If the conventional value of 0 is too open, consider setting a value
-such as 0o022, 0o027, 0o077, or another specific value. Otherwise,
-ensure the daemon creates every file with an explicit access mode for
-the purpose.
-
-
-File descriptors
-================
-
-Why does the output stop after opening the daemon context?
-----------------------------------------------------------
-
-The specified behaviour in `PEP 3143`_ includes the requirement to
-detach the process from the controlling terminal (to allow the process
-to continue to run as a daemon), and to close all file descriptors not
-known to be safe once detached (to ensure any files that continue to
-be used are under the control of the daemon process).
-
-If you want the process to generate output via the system streams
-‘sys.stdout’ and ‘sys.stderr’, set the ‘DaemonContext’'s ‘stdout’
-and/or ‘stderr’ options to a file-like object (e.g. the ‘stream’
-attribute of a ‘logging.Handler’ instance). If these objects have file
-descriptors, they will be preserved when the daemon context opens.
-
-How can I preserve a ‘logging’ handler's file descriptor?
----------------------------------------------------------
-
-The ‘DaemonContext.open’ method conforms to `PEP 3143`_ by closing all
-open file descriptors, but excluding those files specified in the
-‘files_preserve’ option. This option is a list of files or file
-descriptors.
-
-The Python standard library ‘logging’ module provides log handlers
-that write to streams, including to files via the ‘StreamHandler’
-class and its sub-classes. The documentation (both the online `logging
-module documentation`_ and the docstrings for the code) makes no
-mention of a way to get at the stream associated with a handler
-object.
-
-However, looking at the source code for ‘StreamHandler’, in Python 2.5
-as ``/usr/lib/python2.5/logging/__init__.py``, shows a ‘stream’
-attribute that is bound to the stream object. The attribute is not
-marked private (i.e. it is not named with a leading underscore), so we
-can presume it is part of the public API.
-
-That attribute can then be used to specify that a logging handler's
-file descriptor should, when the ‘DaemonContext’ opens, be excluded
-from closure::
-
- import logging
- import daemon
-
- # any subclass of StreamHandler should provide the ‘stream’ attribute.
- lh = logging.handlers.TimedRotatingFileHandler(
- "/var/log/foo.log",
- # …
- )
-
- # … do some logging and other activity …
-
- daemon_context = daemon.DaemonContext()
- daemon_context.files_preserve = [lh.stream]
-
- daemon_context.open()
-
- # … continue as a daemon process …
-
-.. _`logging module documentation`: http://docs.python.org/library/logging
-
-
-..
- This is free software: you may copy, modify, and/or distribute this work
- under the terms of the Apache License version 2.0 as published by the
- Apache Software Foundation.
- No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-..
- Local variables:
- coding: utf-8
- mode: text
- mode: rst
- time-stamp-format: "%:y-%02m-%02d"
- time-stamp-start: "^:Updated:[ ]+"
- time-stamp-end: "$"
- time-stamp-line-limit: 20
- End:
- vim: fileencoding=utf-8 filetype=rst :
diff --git a/external_libs/python/python-daemon-2.0.5/doc/TODO b/external_libs/python/python-daemon-2.0.5/doc/TODO
deleted file mode 100644
index 81b41481..00000000
--- a/external_libs/python/python-daemon-2.0.5/doc/TODO
+++ /dev/null
@@ -1,95 +0,0 @@
-TODO for ‘python-daemon’ library
-################################
-
-:Updated: 2015-01-10
-
-=======
-PENDING
-=======
-
-Tests
-=====
-
-Libraries
-=========
-
-* Evaluate switching to ‘flufl.lock’ library for PID lockfile behaviour
- <http://pypi.python.org/pypi/flufl.lock>_.
-
-Features
-========
-
-Important
----------
-
-Wishlist
---------
-
-* Allow specification of a syslog service name to log as (default:
- output to stdout and stderr, not syslog).
-
-Documentation
-=============
-
-Standard library inclusion
-==========================
-
-
-====
-DONE
-====
-
-* Convert to Python 2 and Python 3 compatible code base.
-
-* Work correctly with current ‘lockfile’ library (0.10 or later).
-
-* Write full unit tests for every new or changed behaviour at time of
- commit.
-
-* Detect whether started by another process that handles
- daemonisation, such as ‘inetd’, and behave appropriately.
-
-* Detach to new process and session group.
-
-* Allow specification of working directory (default: '/').
-
-* Allow specification of umask (default: 0o000).
-
-* Drop ‘suid’ and ‘sgid’ privileges if set.
-
-* Close all open file handles.
-
-* Re-open stdin, stdout, stderr to user-specified files.
-
-* Default re-open stdin, stdout, stderr to ‘/dev/null’.
-
-* Allow specification of a non-root user and group to drop to, if
- started as ‘root’ (default: no change of user or group).
-
-* Implement context manager protocol for daemon context.
-
-* Allow specification of PID file with its own context manager
- (default: no PID file).
-
-* Full docstrings for functions, classes, and modules.
-
-* PEP 3143 for adding this library to the Python standard library.
-
-
-..
- This is free software: you may copy, modify, and/or distribute this work
- under the terms of the Apache License version 2.0 as published by the
- Apache Software Foundation.
- No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-..
- Local variables:
- coding: utf-8
- mode: text
- mode: rst
- time-stamp-format: "%:y-%02m-%02d"
- time-stamp-start: "^:Updated:[ ]+"
- time-stamp-end: "$"
- time-stamp-line-limit: 20
- End:
- vim: fileencoding=utf-8 filetype=rst :
diff --git a/external_libs/python/python-daemon-2.0.5/doc/hacking.txt b/external_libs/python/python-daemon-2.0.5/doc/hacking.txt
deleted file mode 100644
index 9484dbd0..00000000
--- a/external_libs/python/python-daemon-2.0.5/doc/hacking.txt
+++ /dev/null
@@ -1,180 +0,0 @@
-Developer's guide
-#################
-
-:Author: Ben Finney <ben+python@benfinney.id.au>
-:Updated: 2014-11-28
-
-
-Project layout
-==============
-
-::
-
- ./ Top level of source tree
- doc/ Project documentation
- bin/ Executable programs
- daemon/ Main ‘daemon’ library
- test/ Unit tests
-
-
-Code style
-==========
-
-Python
-------
-
-All Python code should conform to the guidelines in PEP8_. In
-particular:
-
-* Indent each level using 4 spaces (``U+0020 SPACE``), and no TABs
- (``U+0008 CHARACTER TABULATION``).
-
-* Name modules in lower case, ``multiplewordslikethis``.
-
-* Name classes in title case, ``MultipleWordsLikeThis``.
-
-* Name functions, instances and other variables in lower case,
- ``multiple_words_like_this``.
-
-* Every module, class, and function has a Python doc string explaining
- its purpose and API.
-
- *Exception*: Functions whose purpose and API are mandated by Python
- itself (dunder-named methods) do not need a doc string.
-
-* Doc strings are written as triple-quoted strings.
-
- * The text of the doc string is marked up with reStructuredText.
-
- * The first line is a one-line synopsis of the object. This summary
- line appears on the same line as the opening triple-quote,
- separated by a single space.
-
- * Further lines, if needed, are separated from the first by one
- blank line.
-
- * The synopsis is separated by one space from the opening
- triple-quote; this causes it to appear four columns past the
- beginning of the line. All subsequent lines are indented at least
- four columns also.
-
- * The synopsis is followed by a reStructuredText field list. The
- field names are: “param foo” for each parameter (where “foo” is
- the parameter name), and “return” for the return value. The field
- values describe the purpose of each.
-
- * The closing triple-quote appears on a separate line.
-
- Example::
-
- def frobnicate(spam, algorithm="dv"):
- """ Perform frobnication on ``spam``.
-
- :param spam: A travortionate (as a sequence of strings).
- :param algorithm: The name of the algorithm to use for
- frobnicating the travortionate.
- :return: The frobnicated travortionate, if it is
- non-empty; otherwise None.
-
- The frobnication is done by the Dietzel-Venkman algorithm,
- and optimises for the case where ``spam`` is freebled and
- agglutinative.
-
- """
- spagnify(spam)
- # …
-
-* All ``import`` statements appear at the top of the module.
-
-* Each ``import`` statement imports a single module, or multiple names
- from a single module.
-
- Example::
-
- import sys
- import os
- from spam import foo, bar, baz
-
-.. _PEP8: http://www.python.org/dev/peps/pep-0008/
-
-Additional style guidelines:
-
-* All text files (including program code) are encoded in UTF-8.
-
-* A page break (``U+000C FORM FEED``) whitespace character is used
- within a module to break up semantically separate areas of the
- module.
-
-* Editor hints for Emacs and Vim appear in a comment block at the
- file's end::
-
-
- # Local variables:
- # coding: utf-8
- # mode: python
- # End:
- # vim: fileencoding=utf-8 filetype=python :
-
-
-Unit tests
-==========
-
-All code should aim for 100% coverage by unit tests. New code, or
-changes to existing code, will only be considered for inclusion in the
-development tree when accompanied by corresponding additions or
-changes to the unit tests.
-
-Test-driven development
------------------------
-
-Where possible, practice test-driven development to implement program
-code.
-
-* During a development session, maintain a separate window or terminal
- with the unit test suite for the project running continuously, or
- automatically every few seconds.
-
-* Any time a test is failing, the only valid change is to make all
- tests pass.
-
-* Develop new interface features (changes to the program unit's
- behaviour) only when all current tests pass.
-
-* Refactor as needed, but only when all tests pass.
-
- * Refactoring is any change to the code which does not alter its
- interface or expected behaviour, such as performance
- optimisations, readability improvements, modularisation
- improvements etc.
-
-* Develop new or changed program behaviour by:
-
- * *First* write a single, specific test case for that new behaviour,
- then watch the test fail in the absence of the desired behaviour.
-
- * Implement the minimum necessary change to satisfy the failing
- test. Continue until all tests pass again, then stop making
- functional changes.
-
- * Once all tests (including the new test) pass, consider refactoring
- the code and the tests immediately, then ensure all the tests pass
- again after any changes.
-
- * Iterate for each incremental change in interface or behaviour.
-
-Test-driven development is not absolutely necessary, but is the
-simplest, most direct way to generate the kind of program changes
-accompanied by unit tests that are necessary for inclusion in the
-project.
-
-
-..
- Local variables:
- coding: utf-8
- mode: rst
- time-stamp-format: "%:y-%02m-%02d"
- time-stamp-start: "^:Updated:[ ]+"
- time-stamp-end: "$"
- time-stamp-line-limit: 20
- End:
- vim: fileencoding=utf-8 filetype=rst :
diff --git a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/PKG-INFO b/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/PKG-INFO
deleted file mode 100644
index fd81f509..00000000
--- a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/PKG-INFO
+++ /dev/null
@@ -1,38 +0,0 @@
-Metadata-Version: 1.1
-Name: python-daemon
-Version: 2.0.5
-Summary: Library to implement a well-behaved Unix daemon process.
-Home-page: https://alioth.debian.org/projects/python-daemon/
-Author: Ben Finney
-Author-email: ben+python@benfinney.id.au
-License: Apache-2
-Description: This library implements the well-behaved daemon specification of
- :pep:`3143`, “Standard daemon process library”.
-
- A well-behaved Unix daemon process is tricky to get right, but the
- required steps are much the same for every daemon program. A
- `DaemonContext` instance holds the behaviour and configured
- process environment for the program; use the instance as a context
- manager to enter a daemon state.
-
- Simple example of usage::
-
- import daemon
-
- from spam import do_main_program
-
- with daemon.DaemonContext():
- do_main_program()
-
- Customisation of the steps to become a daemon is available by
- setting options on the `DaemonContext` instance; see the
- documentation for that class for each option.
-Keywords: daemon,fork,unix
-Platform: UNKNOWN
-Classifier: Development Status :: 4 - Beta
-Classifier: License :: OSI Approved :: Apache Software License
-Classifier: Operating System :: POSIX
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Intended Audience :: Developers
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/SOURCES.txt b/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/SOURCES.txt
deleted file mode 100644
index 6e176719..00000000
--- a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-ChangeLog
-LICENSE.ASF-2
-LICENSE.GPL-3
-MANIFEST.in
-setup.cfg
-setup.py
-test_version.py
-version.py
-daemon/__init__.py
-daemon/_metadata.py
-daemon/daemon.py
-daemon/pidfile.py
-daemon/runner.py
-doc/CREDITS
-doc/FAQ
-doc/TODO
-doc/hacking.txt
-python_daemon.egg-info/PKG-INFO
-python_daemon.egg-info/SOURCES.txt
-python_daemon.egg-info/dependency_links.txt
-python_daemon.egg-info/not-zip-safe
-python_daemon.egg-info/requires.txt
-python_daemon.egg-info/top_level.txt
-python_daemon.egg-info/version_info.json
-test/__init__.py
-test/scaffold.py
-test/test_daemon.py
-test/test_metadata.py
-test/test_pidfile.py
-test/test_runner.py \ No newline at end of file
diff --git a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/dependency_links.txt b/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/dependency_links.txt
deleted file mode 100644
index 8b137891..00000000
--- a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/dependency_links.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/not-zip-safe b/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/not-zip-safe
deleted file mode 100644
index 8b137891..00000000
--- a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/not-zip-safe
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/requires.txt b/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/requires.txt
deleted file mode 100644
index d1496b02..00000000
--- a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/requires.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-setuptools
-docutils
-lockfile >=0.10
diff --git a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/top_level.txt b/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/top_level.txt
deleted file mode 100644
index 28e3ee0c..00000000
--- a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-daemon
diff --git a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/version_info.json b/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/version_info.json
deleted file mode 100644
index bac1b84f..00000000
--- a/external_libs/python/python-daemon-2.0.5/python_daemon.egg-info/version_info.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "release_date": "2015-02-02",
- "version": "2.0.5",
- "maintainer": "Ben Finney <ben+python@benfinney.id.au>",
- "body": "* Refine compatibility of exceptions for file operations.\n* Specify the text encoding when opening the changelog file.\n"
-} \ No newline at end of file
diff --git a/external_libs/python/python-daemon-2.0.5/setup.cfg b/external_libs/python/python-daemon-2.0.5/setup.cfg
deleted file mode 100644
index 9d3d2c02..00000000
--- a/external_libs/python/python-daemon-2.0.5/setup.cfg
+++ /dev/null
@@ -1,11 +0,0 @@
-[aliases]
-distribute = register sdist bdist_wheel upload
-
-[bdist_wheel]
-universal = true
-
-[egg_info]
-tag_svn_revision = 0
-tag_date = 0
-tag_build =
-
diff --git a/external_libs/python/python-daemon-2.0.5/setup.py b/external_libs/python/python-daemon-2.0.5/setup.py
deleted file mode 100644
index 16a6a6a6..00000000
--- a/external_libs/python/python-daemon-2.0.5/setup.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# setup.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
-# Copyright © 2008 Robert Niederreiter, Jens Klein
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation; version 3 of that license or any later version.
-# No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details.
-
-""" Distribution setup for ‘python-daemon’ library. """
-
-from __future__ import (absolute_import, unicode_literals)
-
-import sys
-import os
-import os.path
-import pydoc
-import distutils.util
-
-from setuptools import (setup, find_packages)
-
-import version
-
-
-fromlist_expects_type = str
-if sys.version_info < (3, 0):
- fromlist_expects_type = bytes
-
-
-main_module_name = 'daemon'
-main_module_fromlist = list(map(fromlist_expects_type, [
- '_metadata']))
-main_module = __import__(
- main_module_name,
- level=0, fromlist=main_module_fromlist)
-metadata = main_module._metadata
-
-(synopsis, long_description) = pydoc.splitdoc(pydoc.getdoc(main_module))
-
-version_info = metadata.get_distribution_version_info()
-version_string = version_info['version']
-
-(maintainer_name, maintainer_email) = metadata.parse_person_field(
- version_info['maintainer'])
-
-
-setup(
- name=metadata.distribution_name,
- version=version_string,
- packages=find_packages(exclude=["test"]),
- cmdclass={
- "write_version_info": version.WriteVersionInfoCommand,
- "egg_info": version.EggInfoCommand,
- },
-
- # Setuptools metadata.
- maintainer=maintainer_name,
- maintainer_email=maintainer_email,
- zip_safe=False,
- setup_requires=[
- "docutils",
- ],
- test_suite="unittest2.collector",
- tests_require=[
- "unittest2 >=0.6",
- "testtools",
- "testscenarios >=0.4",
- "mock >=1.0",
- "docutils",
- ],
- install_requires=[
- "setuptools",
- "docutils",
- "lockfile >=0.10",
- ],
-
- # PyPI metadata.
- author=metadata.author_name,
- author_email=metadata.author_email,
- description=synopsis,
- license=metadata.license,
- keywords="daemon fork unix".split(),
- url=metadata.url,
- long_description=long_description,
- classifiers=[
- # Reference: http://pypi.python.org/pypi?%3Aaction=list_classifiers
- "Development Status :: 4 - Beta",
- "License :: OSI Approved :: Apache Software License",
- "Operating System :: POSIX",
- "Programming Language :: Python :: 2.7",
- "Programming Language :: Python :: 3",
- "Intended Audience :: Developers",
- "Topic :: Software Development :: Libraries :: Python Modules",
- ],
- )
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/external_libs/python/python-daemon-2.0.5/test/__init__.py b/external_libs/python/python-daemon-2.0.5/test/__init__.py
deleted file mode 100644
index 398519f1..00000000
--- a/external_libs/python/python-daemon-2.0.5/test/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# test/__init__.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the Apache License, version 2.0 as published by the
-# Apache Software Foundation.
-# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-""" Unit test suite for ‘daemon’ package.
- """
-
-from __future__ import (absolute_import, unicode_literals)
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/external_libs/python/python-daemon-2.0.5/test/scaffold.py b/external_libs/python/python-daemon-2.0.5/test/scaffold.py
deleted file mode 100644
index 9a4f1150..00000000
--- a/external_libs/python/python-daemon-2.0.5/test/scaffold.py
+++ /dev/null
@@ -1,322 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# test/scaffold.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2007–2015 Ben Finney <ben+python@benfinney.id.au>
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the Apache License, version 2.0 as published by the
-# Apache Software Foundation.
-# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-""" Scaffolding for unit test modules.
- """
-
-from __future__ import (absolute_import, unicode_literals)
-
-import unittest
-import doctest
-import logging
-import os
-import sys
-import operator
-import textwrap
-from copy import deepcopy
-import functools
-
-try:
- # Python 2 has both ‘str’ (bytes) and ‘unicode’ (text).
- basestring = basestring
- unicode = unicode
-except NameError:
- # Python 3 names the Unicode data type ‘str’.
- basestring = str
- unicode = str
-
-import testscenarios
-import testtools.testcase
-
-
-test_dir = os.path.dirname(os.path.abspath(__file__))
-parent_dir = os.path.dirname(test_dir)
-if not test_dir in sys.path:
- sys.path.insert(1, test_dir)
-if not parent_dir in sys.path:
- sys.path.insert(1, parent_dir)
-
-# Disable all but the most critical logging messages.
-logging.disable(logging.CRITICAL)
-
-
-def get_function_signature(func):
- """ Get the function signature as a mapping of attributes.
-
- :param func: The function object to interrogate.
- :return: A mapping of the components of a function signature.
-
- The signature is constructed as a mapping:
-
- * 'name': The function's defined name.
- * 'arg_count': The number of arguments expected by the function.
- * 'arg_names': A sequence of the argument names, as strings.
- * 'arg_defaults': A sequence of the default values for the arguments.
- * 'va_args': The name bound to remaining positional arguments.
- * 'va_kw_args': The name bound to remaining keyword arguments.
-
- """
- try:
- # Python 3 function attributes.
- func_code = func.__code__
- func_defaults = func.__defaults__
- except AttributeError:
- # Python 2 function attributes.
- func_code = func.func_code
- func_defaults = func.func_defaults
-
- arg_count = func_code.co_argcount
- arg_names = func_code.co_varnames[:arg_count]
-
- arg_defaults = {}
- if func_defaults is not None:
- arg_defaults = dict(
- (name, value)
- for (name, value) in
- zip(arg_names[::-1], func_defaults[::-1]))
-
- signature = {
- 'name': func.__name__,
- 'arg_count': arg_count,
- 'arg_names': arg_names,
- 'arg_defaults': arg_defaults,
- }
-
- non_pos_names = list(func_code.co_varnames[arg_count:])
- COLLECTS_ARBITRARY_POSITIONAL_ARGS = 0x04
- if func_code.co_flags & COLLECTS_ARBITRARY_POSITIONAL_ARGS:
- signature['var_args'] = non_pos_names.pop(0)
- COLLECTS_ARBITRARY_KEYWORD_ARGS = 0x08
- if func_code.co_flags & COLLECTS_ARBITRARY_KEYWORD_ARGS:
- signature['var_kw_args'] = non_pos_names.pop(0)
-
- return signature
-
-
-def format_function_signature(func):
- """ Format the function signature as printable text.
-
- :param func: The function object to interrogate.
- :return: A formatted text representation of the function signature.
-
- The signature is rendered a text; for example::
-
- foo(spam, eggs, ham=True, beans=None, *args, **kwargs)
-
- """
- signature = get_function_signature(func)
-
- args_text = []
- for arg_name in signature['arg_names']:
- if arg_name in signature['arg_defaults']:
- arg_text = "{name}={value!r}".format(
- name=arg_name, value=signature['arg_defaults'][arg_name])
- else:
- arg_text = "{name}".format(
- name=arg_name)
- args_text.append(arg_text)
- if 'var_args' in signature:
- args_text.append("*{var_args}".format(signature))
- if 'var_kw_args' in signature:
- args_text.append("**{var_kw_args}".format(signature))
- signature_args_text = ", ".join(args_text)
-
- func_name = signature['name']
- signature_text = "{name}({args})".format(
- name=func_name, args=signature_args_text)
-
- return signature_text
-
-
-class TestCase(testtools.testcase.TestCase):
- """ Test case behaviour. """
-
- def failUnlessOutputCheckerMatch(self, want, got, msg=None):
- """ Fail unless the specified string matches the expected.
-
- :param want: The desired output pattern.
- :param got: The actual text to match.
- :param msg: A message to prefix on the failure message.
- :return: ``None``.
- :raises self.failureException: If the text does not match.
-
- Fail the test unless ``want`` matches ``got``, as determined by
- a ``doctest.OutputChecker`` instance. This is not an equality
- check, but a pattern match according to the ``OutputChecker``
- rules.
-
- """
- checker = doctest.OutputChecker()
- want = textwrap.dedent(want)
- source = ""
- example = doctest.Example(source, want)
- got = textwrap.dedent(got)
- checker_optionflags = functools.reduce(operator.or_, [
- doctest.ELLIPSIS,
- ])
- if not checker.check_output(want, got, checker_optionflags):
- if msg is None:
- diff = checker.output_difference(
- example, got, checker_optionflags)
- msg = "\n".join([
- "Output received did not match expected output",
- "{diff}",
- ]).format(
- diff=diff)
- raise self.failureException(msg)
-
- assertOutputCheckerMatch = failUnlessOutputCheckerMatch
-
- def failUnlessFunctionInTraceback(self, traceback, function, msg=None):
- """ Fail if the function is not in the traceback.
-
- :param traceback: The traceback object to interrogate.
- :param function: The function object to match.
- :param msg: A message to prefix on the failure message.
- :return: ``None``.
-
- :raises self.failureException: If the function is not in the
- traceback.
-
- Fail the test if the function ``function`` is not at any of the
- levels in the traceback object ``traceback``.
-
- """
- func_in_traceback = False
- expected_code = function.func_code
- current_traceback = traceback
- while current_traceback is not None:
- if expected_code is current_traceback.tb_frame.f_code:
- func_in_traceback = True
- break
- current_traceback = current_traceback.tb_next
-
- if not func_in_traceback:
- if msg is None:
- msg = (
- "Traceback did not lead to original function"
- " {function}"
- ).format(
- function=function)
- raise self.failureException(msg)
-
- assertFunctionInTraceback = failUnlessFunctionInTraceback
-
- def failUnlessFunctionSignatureMatch(self, first, second, msg=None):
- """ Fail if the function signatures do not match.
-
- :param first: The first function to compare.
- :param second: The second function to compare.
- :param msg: A message to prefix to the failure message.
- :return: ``None``.
-
- :raises self.failureException: If the function signatures do
- not match.
-
- Fail the test if the function signature does not match between
- the ``first`` function and the ``second`` function.
-
- The function signature includes:
-
- * function name,
-
- * count of named parameters,
-
- * sequence of named parameters,
-
- * default values of named parameters,
-
- * collector for arbitrary positional arguments,
-
- * collector for arbitrary keyword arguments.
-
- """
- first_signature = get_function_signature(first)
- second_signature = get_function_signature(second)
-
- if first_signature != second_signature:
- if msg is None:
- first_signature_text = format_function_signature(first)
- second_signature_text = format_function_signature(second)
- msg = (textwrap.dedent("""\
- Function signatures do not match:
- {first!r} != {second!r}
- Expected:
- {first_text}
- Got:
- {second_text}""")
- ).format(
- first=first_signature,
- first_text=first_signature_text,
- second=second_signature,
- second_text=second_signature_text,
- )
- raise self.failureException(msg)
-
- assertFunctionSignatureMatch = failUnlessFunctionSignatureMatch
-
-
-class TestCaseWithScenarios(testscenarios.WithScenarios, TestCase):
- """ Test cases run per scenario. """
-
-
-class Exception_TestCase(TestCaseWithScenarios):
- """ Test cases for exception classes. """
-
- def test_exception_instance(self):
- """ Exception instance should be created. """
- self.assertIsNot(self.instance, None)
-
- def test_exception_types(self):
- """ Exception instance should match expected types. """
- for match_type in self.types:
- self.assertIsInstance(self.instance, match_type)
-
-
-def make_exception_scenarios(scenarios):
- """ Make test scenarios for exception classes.
-
- :param scenarios: Sequence of scenarios.
- :return: List of scenarios with additional mapping entries.
-
- Use this with `testscenarios` to adapt `Exception_TestCase`_ for
- any exceptions that need testing.
-
- Each scenario is a tuple (`name`, `map`) where `map` is a mapping
- of attributes to be applied to each test case. Attributes map must
- contain items for:
-
- :key exc_type:
- The exception type to be tested.
- :key min_args:
- The minimum argument count for the exception instance
- initialiser.
- :key types:
- Sequence of types that should be superclasses of each
- instance of the exception type.
-
- """
- updated_scenarios = deepcopy(scenarios)
- for (name, scenario) in updated_scenarios:
- args = (None,) * scenario['min_args']
- scenario['args'] = args
- instance = scenario['exc_type'](*args)
- scenario['instance'] = instance
-
- return updated_scenarios
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/external_libs/python/python-daemon-2.0.5/test/test_daemon.py b/external_libs/python/python-daemon-2.0.5/test/test_daemon.py
deleted file mode 100644
index a911858a..00000000
--- a/external_libs/python/python-daemon-2.0.5/test/test_daemon.py
+++ /dev/null
@@ -1,1744 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# test/test_daemon.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the Apache License, version 2.0 as published by the
-# Apache Software Foundation.
-# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-""" Unit test for ‘daemon’ module.
- """
-
-from __future__ import (absolute_import, unicode_literals)
-
-import os
-import sys
-import tempfile
-import resource
-import errno
-import signal
-import socket
-from types import ModuleType
-import collections
-import functools
-try:
- # Standard library of Python 2.7 and later.
- from io import StringIO
-except ImportError:
- # Standard library of Python 2.6 and earlier.
- from StringIO import StringIO
-
-import mock
-
-from . import scaffold
-from .scaffold import (basestring, unicode)
-from .test_pidfile import (
- FakeFileDescriptorStringIO,
- setup_pidfile_fixtures,
- )
-
-import daemon
-
-
-class ModuleExceptions_TestCase(scaffold.Exception_TestCase):
- """ Test cases for module exception classes. """
-
- scenarios = scaffold.make_exception_scenarios([
- ('daemon.daemon.DaemonError', dict(
- exc_type = daemon.daemon.DaemonError,
- min_args = 1,
- types = [Exception],
- )),
- ('daemon.daemon.DaemonOSEnvironmentError', dict(
- exc_type = daemon.daemon.DaemonOSEnvironmentError,
- min_args = 1,
- types = [daemon.daemon.DaemonError, OSError],
- )),
- ('daemon.daemon.DaemonProcessDetachError', dict(
- exc_type = daemon.daemon.DaemonProcessDetachError,
- min_args = 1,
- types = [daemon.daemon.DaemonError, OSError],
- )),
- ])
-
-
-def setup_daemon_context_fixtures(testcase):
- """ Set up common test fixtures for DaemonContext test case.
-
- :param testcase: A ``TestCase`` instance to decorate.
- :return: ``None``.
-
- Decorate the `testcase` with fixtures for tests involving
- `DaemonContext`.
-
- """
- setup_streams_fixtures(testcase)
-
- setup_pidfile_fixtures(testcase)
-
- testcase.fake_pidfile_path = tempfile.mktemp()
- testcase.mock_pidlockfile = mock.MagicMock()
- testcase.mock_pidlockfile.path = testcase.fake_pidfile_path
-
- testcase.daemon_context_args = dict(
- stdin=testcase.stream_files_by_name['stdin'],
- stdout=testcase.stream_files_by_name['stdout'],
- stderr=testcase.stream_files_by_name['stderr'],
- )
- testcase.test_instance = daemon.DaemonContext(
- **testcase.daemon_context_args)
-
-fake_default_signal_map = object()
-
-@mock.patch.object(
- daemon.daemon, "is_detach_process_context_required",
- new=(lambda: True))
-@mock.patch.object(
- daemon.daemon, "make_default_signal_map",
- new=(lambda: fake_default_signal_map))
-@mock.patch.object(os, "setgid", new=(lambda x: object()))
-@mock.patch.object(os, "setuid", new=(lambda x: object()))
-class DaemonContext_BaseTestCase(scaffold.TestCase):
- """ Base class for DaemonContext test case classes. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(DaemonContext_BaseTestCase, self).setUp()
-
- setup_daemon_context_fixtures(self)
-
-
-class DaemonContext_TestCase(DaemonContext_BaseTestCase):
- """ Test cases for DaemonContext class. """
-
- def test_instantiate(self):
- """ New instance of DaemonContext should be created. """
- self.assertIsInstance(
- self.test_instance, daemon.daemon.DaemonContext)
-
- def test_minimum_zero_arguments(self):
- """ Initialiser should not require any arguments. """
- instance = daemon.daemon.DaemonContext()
- self.assertIsNot(instance, None)
-
- def test_has_specified_chroot_directory(self):
- """ Should have specified chroot_directory option. """
- args = dict(
- chroot_directory=object(),
- )
- expected_directory = args['chroot_directory']
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_directory, instance.chroot_directory)
-
- def test_has_specified_working_directory(self):
- """ Should have specified working_directory option. """
- args = dict(
- working_directory=object(),
- )
- expected_directory = args['working_directory']
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_directory, instance.working_directory)
-
- def test_has_default_working_directory(self):
- """ Should have default working_directory option. """
- args = dict()
- expected_directory = "/"
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_directory, instance.working_directory)
-
- def test_has_specified_creation_mask(self):
- """ Should have specified umask option. """
- args = dict(
- umask=object(),
- )
- expected_mask = args['umask']
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_mask, instance.umask)
-
- def test_has_default_creation_mask(self):
- """ Should have default umask option. """
- args = dict()
- expected_mask = 0
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_mask, instance.umask)
-
- def test_has_specified_uid(self):
- """ Should have specified uid option. """
- args = dict(
- uid=object(),
- )
- expected_id = args['uid']
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_id, instance.uid)
-
- def test_has_derived_uid(self):
- """ Should have uid option derived from process. """
- args = dict()
- expected_id = os.getuid()
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_id, instance.uid)
-
- def test_has_specified_gid(self):
- """ Should have specified gid option. """
- args = dict(
- gid=object(),
- )
- expected_id = args['gid']
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_id, instance.gid)
-
- def test_has_derived_gid(self):
- """ Should have gid option derived from process. """
- args = dict()
- expected_id = os.getgid()
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_id, instance.gid)
-
- def test_has_specified_detach_process(self):
- """ Should have specified detach_process option. """
- args = dict(
- detach_process=object(),
- )
- expected_value = args['detach_process']
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_value, instance.detach_process)
-
- def test_has_derived_detach_process(self):
- """ Should have detach_process option derived from environment. """
- args = dict()
- func = daemon.daemon.is_detach_process_context_required
- expected_value = func()
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_value, instance.detach_process)
-
- def test_has_specified_files_preserve(self):
- """ Should have specified files_preserve option. """
- args = dict(
- files_preserve=object(),
- )
- expected_files_preserve = args['files_preserve']
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_files_preserve, instance.files_preserve)
-
- def test_has_specified_pidfile(self):
- """ Should have the specified pidfile. """
- args = dict(
- pidfile=object(),
- )
- expected_pidfile = args['pidfile']
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_pidfile, instance.pidfile)
-
- def test_has_specified_stdin(self):
- """ Should have specified stdin option. """
- args = dict(
- stdin=object(),
- )
- expected_file = args['stdin']
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_file, instance.stdin)
-
- def test_has_specified_stdout(self):
- """ Should have specified stdout option. """
- args = dict(
- stdout=object(),
- )
- expected_file = args['stdout']
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_file, instance.stdout)
-
- def test_has_specified_stderr(self):
- """ Should have specified stderr option. """
- args = dict(
- stderr=object(),
- )
- expected_file = args['stderr']
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_file, instance.stderr)
-
- def test_has_specified_signal_map(self):
- """ Should have specified signal_map option. """
- args = dict(
- signal_map=object(),
- )
- expected_signal_map = args['signal_map']
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_signal_map, instance.signal_map)
-
- def test_has_derived_signal_map(self):
- """ Should have signal_map option derived from system. """
- args = dict()
- expected_signal_map = daemon.daemon.make_default_signal_map()
- instance = daemon.daemon.DaemonContext(**args)
- self.assertEqual(expected_signal_map, instance.signal_map)
-
-
-class DaemonContext_is_open_TestCase(DaemonContext_BaseTestCase):
- """ Test cases for DaemonContext.is_open property. """
-
- def test_begin_false(self):
- """ Initial value of is_open should be False. """
- instance = self.test_instance
- self.assertEqual(False, instance.is_open)
-
- def test_write_fails(self):
- """ Writing to is_open should fail. """
- instance = self.test_instance
- self.assertRaises(
- AttributeError,
- setattr, instance, 'is_open', object())
-
-
-class DaemonContext_open_TestCase(DaemonContext_BaseTestCase):
- """ Test cases for DaemonContext.open method. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(DaemonContext_open_TestCase, self).setUp()
-
- self.test_instance._is_open = False
-
- self.mock_module_daemon = mock.MagicMock()
- daemon_func_patchers = dict(
- (func_name, mock.patch.object(
- daemon.daemon, func_name))
- for func_name in [
- "detach_process_context",
- "change_working_directory",
- "change_root_directory",
- "change_file_creation_mask",
- "change_process_owner",
- "prevent_core_dump",
- "close_all_open_files",
- "redirect_stream",
- "set_signal_handlers",
- "register_atexit_function",
- ])
- for (func_name, patcher) in daemon_func_patchers.items():
- mock_func = patcher.start()
- self.addCleanup(patcher.stop)
- self.mock_module_daemon.attach_mock(mock_func, func_name)
-
- self.mock_module_daemon.attach_mock(mock.Mock(), 'DaemonContext')
-
- self.test_files_preserve_fds = object()
- self.test_signal_handler_map = object()
- daemoncontext_method_return_values = {
- '_get_exclude_file_descriptors':
- self.test_files_preserve_fds,
- '_make_signal_handler_map':
- self.test_signal_handler_map,
- }
- daemoncontext_func_patchers = dict(
- (func_name, mock.patch.object(
- daemon.daemon.DaemonContext,
- func_name,
- return_value=return_value))
- for (func_name, return_value) in
- daemoncontext_method_return_values.items())
- for (func_name, patcher) in daemoncontext_func_patchers.items():
- mock_func = patcher.start()
- self.addCleanup(patcher.stop)
- self.mock_module_daemon.DaemonContext.attach_mock(
- mock_func, func_name)
-
- def test_performs_steps_in_expected_sequence(self):
- """ Should perform daemonisation steps in expected sequence. """
- instance = self.test_instance
- instance.chroot_directory = object()
- instance.detach_process = True
- instance.pidfile = self.mock_pidlockfile
- self.mock_module_daemon.attach_mock(
- self.mock_pidlockfile, 'pidlockfile')
- expected_calls = [
- mock.call.change_root_directory(mock.ANY),
- mock.call.prevent_core_dump(),
- mock.call.change_file_creation_mask(mock.ANY),
- mock.call.change_working_directory(mock.ANY),
- mock.call.change_process_owner(mock.ANY, mock.ANY),
- mock.call.detach_process_context(),
- mock.call.DaemonContext._make_signal_handler_map(),
- mock.call.set_signal_handlers(mock.ANY),
- mock.call.DaemonContext._get_exclude_file_descriptors(),
- mock.call.close_all_open_files(exclude=mock.ANY),
- mock.call.redirect_stream(mock.ANY, mock.ANY),
- mock.call.redirect_stream(mock.ANY, mock.ANY),
- mock.call.redirect_stream(mock.ANY, mock.ANY),
- mock.call.pidlockfile.__enter__(),
- mock.call.register_atexit_function(mock.ANY),
- ]
- instance.open()
- self.mock_module_daemon.assert_has_calls(expected_calls)
-
- def test_returns_immediately_if_is_open(self):
- """ Should return immediately if is_open property is true. """
- instance = self.test_instance
- instance._is_open = True
- instance.open()
- self.assertEqual(0, len(self.mock_module_daemon.mock_calls))
-
- def test_changes_root_directory_to_chroot_directory(self):
- """ Should change root directory to `chroot_directory` option. """
- instance = self.test_instance
- chroot_directory = object()
- instance.chroot_directory = chroot_directory
- instance.open()
- self.mock_module_daemon.change_root_directory.assert_called_with(
- chroot_directory)
-
- def test_omits_chroot_if_no_chroot_directory(self):
- """ Should omit changing root directory if no `chroot_directory`. """
- instance = self.test_instance
- instance.chroot_directory = None
- instance.open()
- self.assertFalse(self.mock_module_daemon.change_root_directory.called)
-
- def test_prevents_core_dump(self):
- """ Should request prevention of core dumps. """
- instance = self.test_instance
- instance.open()
- self.mock_module_daemon.prevent_core_dump.assert_called_with()
-
- def test_omits_prevent_core_dump_if_prevent_core_false(self):
- """ Should omit preventing core dumps if `prevent_core` is false. """
- instance = self.test_instance
- instance.prevent_core = False
- instance.open()
- self.assertFalse(self.mock_module_daemon.prevent_core_dump.called)
-
- def test_closes_open_files(self):
- """ Should close all open files, excluding `files_preserve`. """
- instance = self.test_instance
- expected_exclude = self.test_files_preserve_fds
- instance.open()
- self.mock_module_daemon.close_all_open_files.assert_called_with(
- exclude=expected_exclude)
-
- def test_changes_directory_to_working_directory(self):
- """ Should change current directory to `working_directory` option. """
- instance = self.test_instance
- working_directory = object()
- instance.working_directory = working_directory
- instance.open()
- self.mock_module_daemon.change_working_directory.assert_called_with(
- working_directory)
-
- def test_changes_creation_mask_to_umask(self):
- """ Should change file creation mask to `umask` option. """
- instance = self.test_instance
- umask = object()
- instance.umask = umask
- instance.open()
- self.mock_module_daemon.change_file_creation_mask.assert_called_with(
- umask)
-
- def test_changes_owner_to_specified_uid_and_gid(self):
- """ Should change process UID and GID to `uid` and `gid` options. """
- instance = self.test_instance
- uid = object()
- gid = object()
- instance.uid = uid
- instance.gid = gid
- instance.open()
- self.mock_module_daemon.change_process_owner.assert_called_with(
- uid, gid)
-
- def test_detaches_process_context(self):
- """ Should request detach of process context. """
- instance = self.test_instance
- instance.open()
- self.mock_module_daemon.detach_process_context.assert_called_with()
-
- def test_omits_process_detach_if_not_required(self):
- """ Should omit detach of process context if not required. """
- instance = self.test_instance
- instance.detach_process = False
- instance.open()
- self.assertFalse(self.mock_module_daemon.detach_process_context.called)
-
- def test_sets_signal_handlers_from_signal_map(self):
- """ Should set signal handlers according to `signal_map`. """
- instance = self.test_instance
- instance.signal_map = object()
- expected_signal_handler_map = self.test_signal_handler_map
- instance.open()
- self.mock_module_daemon.set_signal_handlers.assert_called_with(
- expected_signal_handler_map)
-
- def test_redirects_standard_streams(self):
- """ Should request redirection of standard stream files. """
- instance = self.test_instance
- (system_stdin, system_stdout, system_stderr) = (
- sys.stdin, sys.stdout, sys.stderr)
- (target_stdin, target_stdout, target_stderr) = (
- self.stream_files_by_name[name]
- for name in ['stdin', 'stdout', 'stderr'])
- expected_calls = [
- mock.call(system_stdin, target_stdin),
- mock.call(system_stdout, target_stdout),
- mock.call(system_stderr, target_stderr),
- ]
- instance.open()
- self.mock_module_daemon.redirect_stream.assert_has_calls(
- expected_calls, any_order=True)
-
- def test_enters_pidfile_context(self):
- """ Should enter the PID file context manager. """
- instance = self.test_instance
- instance.pidfile = self.mock_pidlockfile
- instance.open()
- self.mock_pidlockfile.__enter__.assert_called_with()
-
- def test_sets_is_open_true(self):
- """ Should set the `is_open` property to True. """
- instance = self.test_instance
- instance.open()
- self.assertEqual(True, instance.is_open)
-
- def test_registers_close_method_for_atexit(self):
- """ Should register the `close` method for atexit processing. """
- instance = self.test_instance
- close_method = instance.close
- instance.open()
- self.mock_module_daemon.register_atexit_function.assert_called_with(
- close_method)
-
-
-class DaemonContext_close_TestCase(DaemonContext_BaseTestCase):
- """ Test cases for DaemonContext.close method. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(DaemonContext_close_TestCase, self).setUp()
-
- self.test_instance._is_open = True
-
- def test_returns_immediately_if_not_is_open(self):
- """ Should return immediately if is_open property is false. """
- instance = self.test_instance
- instance._is_open = False
- instance.pidfile = object()
- instance.close()
- self.assertFalse(self.mock_pidlockfile.__exit__.called)
-
- def test_exits_pidfile_context(self):
- """ Should exit the PID file context manager. """
- instance = self.test_instance
- instance.pidfile = self.mock_pidlockfile
- instance.close()
- self.mock_pidlockfile.__exit__.assert_called_with(None, None, None)
-
- def test_returns_none(self):
- """ Should return None. """
- instance = self.test_instance
- expected_result = None
- result = instance.close()
- self.assertIs(result, expected_result)
-
- def test_sets_is_open_false(self):
- """ Should set the `is_open` property to False. """
- instance = self.test_instance
- instance.close()
- self.assertEqual(False, instance.is_open)
-
-
-@mock.patch.object(daemon.daemon.DaemonContext, "open")
-class DaemonContext_context_manager_enter_TestCase(DaemonContext_BaseTestCase):
- """ Test cases for DaemonContext.__enter__ method. """
-
- def test_opens_daemon_context(self, mock_func_daemoncontext_open):
- """ Should open the DaemonContext. """
- instance = self.test_instance
- instance.__enter__()
- mock_func_daemoncontext_open.assert_called_with()
-
- def test_returns_self_instance(self, mock_func_daemoncontext_open):
- """ Should return DaemonContext instance. """
- instance = self.test_instance
- expected_result = instance
- result = instance.__enter__()
- self.assertIs(result, expected_result)
-
-
-@mock.patch.object(daemon.daemon.DaemonContext, "close")
-class DaemonContext_context_manager_exit_TestCase(DaemonContext_BaseTestCase):
- """ Test cases for DaemonContext.__exit__ method. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(DaemonContext_context_manager_exit_TestCase, self).setUp()
-
- self.test_args = dict(
- exc_type=object(),
- exc_value=object(),
- traceback=object(),
- )
-
- def test_closes_daemon_context(self, mock_func_daemoncontext_close):
- """ Should close the DaemonContext. """
- instance = self.test_instance
- args = self.test_args
- instance.__exit__(**args)
- mock_func_daemoncontext_close.assert_called_with()
-
- def test_returns_none(self, mock_func_daemoncontext_close):
- """ Should return None, indicating exception was not handled. """
- instance = self.test_instance
- args = self.test_args
- expected_result = None
- result = instance.__exit__(**args)
- self.assertIs(result, expected_result)
-
-
-class DaemonContext_terminate_TestCase(DaemonContext_BaseTestCase):
- """ Test cases for DaemonContext.terminate method. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(DaemonContext_terminate_TestCase, self).setUp()
-
- self.test_signal = signal.SIGTERM
- self.test_frame = None
- self.test_args = (self.test_signal, self.test_frame)
-
- def test_raises_system_exit(self):
- """ Should raise SystemExit. """
- instance = self.test_instance
- args = self.test_args
- expected_exception = SystemExit
- self.assertRaises(
- expected_exception,
- instance.terminate, *args)
-
- def test_exception_message_contains_signal_number(self):
- """ Should raise exception with a message containing signal number. """
- instance = self.test_instance
- args = self.test_args
- signal_number = self.test_signal
- expected_exception = SystemExit
- exc = self.assertRaises(
- expected_exception,
- instance.terminate, *args)
- self.assertIn(unicode(signal_number), unicode(exc))
-
-
-class DaemonContext_get_exclude_file_descriptors_TestCase(
- DaemonContext_BaseTestCase):
- """ Test cases for DaemonContext._get_exclude_file_descriptors function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(
- DaemonContext_get_exclude_file_descriptors_TestCase,
- self).setUp()
-
- self.test_files = {
- 2: FakeFileDescriptorStringIO(),
- 5: 5,
- 11: FakeFileDescriptorStringIO(),
- 17: None,
- 23: FakeFileDescriptorStringIO(),
- 37: 37,
- 42: FakeFileDescriptorStringIO(),
- }
- for (fileno, item) in self.test_files.items():
- if hasattr(item, '_fileno'):
- item._fileno = fileno
- self.test_file_descriptors = set(
- fd for (fd, item) in self.test_files.items()
- if item is not None)
- self.test_file_descriptors.update(
- self.stream_files_by_name[name].fileno()
- for name in ['stdin', 'stdout', 'stderr']
- )
-
- def test_returns_expected_file_descriptors(self):
- """ Should return expected set of file descriptors. """
- instance = self.test_instance
- instance.files_preserve = list(self.test_files.values())
- expected_result = self.test_file_descriptors
- result = instance._get_exclude_file_descriptors()
- self.assertEqual(expected_result, result)
-
- def test_returns_stream_redirects_if_no_files_preserve(self):
- """ Should return only stream redirects if no files_preserve. """
- instance = self.test_instance
- instance.files_preserve = None
- expected_result = set(
- stream.fileno()
- for stream in self.stream_files_by_name.values())
- result = instance._get_exclude_file_descriptors()
- self.assertEqual(expected_result, result)
-
- def test_returns_empty_set_if_no_files(self):
- """ Should return empty set if no file options. """
- instance = self.test_instance
- for name in ['files_preserve', 'stdin', 'stdout', 'stderr']:
- setattr(instance, name, None)
- expected_result = set()
- result = instance._get_exclude_file_descriptors()
- self.assertEqual(expected_result, result)
-
- def test_omits_non_file_streams(self):
- """ Should omit non-file stream attributes. """
- instance = self.test_instance
- instance.files_preserve = list(self.test_files.values())
- stream_files = self.stream_files_by_name
- expected_result = self.test_file_descriptors.copy()
- for (pseudo_stream_name, pseudo_stream) in stream_files.items():
- test_non_file_object = object()
- setattr(instance, pseudo_stream_name, test_non_file_object)
- stream_fd = pseudo_stream.fileno()
- expected_result.discard(stream_fd)
- result = instance._get_exclude_file_descriptors()
- self.assertEqual(expected_result, result)
-
- def test_includes_verbatim_streams_without_file_descriptor(self):
- """ Should include verbatim any stream without a file descriptor. """
- instance = self.test_instance
- instance.files_preserve = list(self.test_files.values())
- stream_files = self.stream_files_by_name
- mock_fileno_method = mock.MagicMock(
- spec=sys.__stdin__.fileno,
- side_effect=ValueError)
- expected_result = self.test_file_descriptors.copy()
- for (pseudo_stream_name, pseudo_stream) in stream_files.items():
- test_non_fd_stream = StringIO()
- if not hasattr(test_non_fd_stream, 'fileno'):
- # Python < 3 StringIO doesn't have ‘fileno’ at all.
- # Add a method which raises an exception.
- test_non_fd_stream.fileno = mock_fileno_method
- setattr(instance, pseudo_stream_name, test_non_fd_stream)
- stream_fd = pseudo_stream.fileno()
- expected_result.discard(stream_fd)
- expected_result.add(test_non_fd_stream)
- result = instance._get_exclude_file_descriptors()
- self.assertEqual(expected_result, result)
-
- def test_omits_none_streams(self):
- """ Should omit any stream attribute which is None. """
- instance = self.test_instance
- instance.files_preserve = list(self.test_files.values())
- stream_files = self.stream_files_by_name
- expected_result = self.test_file_descriptors.copy()
- for (pseudo_stream_name, pseudo_stream) in stream_files.items():
- setattr(instance, pseudo_stream_name, None)
- stream_fd = pseudo_stream.fileno()
- expected_result.discard(stream_fd)
- result = instance._get_exclude_file_descriptors()
- self.assertEqual(expected_result, result)
-
-
-class DaemonContext_make_signal_handler_TestCase(DaemonContext_BaseTestCase):
- """ Test cases for DaemonContext._make_signal_handler function. """
-
- def test_returns_ignore_for_none(self):
- """ Should return SIG_IGN when None handler specified. """
- instance = self.test_instance
- target = None
- expected_result = signal.SIG_IGN
- result = instance._make_signal_handler(target)
- self.assertEqual(expected_result, result)
-
- def test_returns_method_for_name(self):
- """ Should return method of DaemonContext when name specified. """
- instance = self.test_instance
- target = 'terminate'
- expected_result = instance.terminate
- result = instance._make_signal_handler(target)
- self.assertEqual(expected_result, result)
-
- def test_raises_error_for_unknown_name(self):
- """ Should raise AttributeError for unknown method name. """
- instance = self.test_instance
- target = 'b0gUs'
- expected_error = AttributeError
- self.assertRaises(
- expected_error,
- instance._make_signal_handler, target)
-
- def test_returns_object_for_object(self):
- """ Should return same object for any other object. """
- instance = self.test_instance
- target = object()
- expected_result = target
- result = instance._make_signal_handler(target)
- self.assertEqual(expected_result, result)
-
-
-class DaemonContext_make_signal_handler_map_TestCase(
- DaemonContext_BaseTestCase):
- """ Test cases for DaemonContext._make_signal_handler_map function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(DaemonContext_make_signal_handler_map_TestCase, self).setUp()
-
- self.test_instance.signal_map = {
- object(): object(),
- object(): object(),
- object(): object(),
- }
-
- self.test_signal_handlers = dict(
- (key, object())
- for key in self.test_instance.signal_map.values())
- self.test_signal_handler_map = dict(
- (key, self.test_signal_handlers[target])
- for (key, target) in self.test_instance.signal_map.items())
-
- def fake_make_signal_handler(target):
- return self.test_signal_handlers[target]
-
- func_patcher_make_signal_handler = mock.patch.object(
- daemon.daemon.DaemonContext, "_make_signal_handler",
- side_effect=fake_make_signal_handler)
- self.mock_func_make_signal_handler = (
- func_patcher_make_signal_handler.start())
- self.addCleanup(func_patcher_make_signal_handler.stop)
-
- def test_returns_constructed_signal_handler_items(self):
- """ Should return items as constructed via make_signal_handler. """
- instance = self.test_instance
- expected_result = self.test_signal_handler_map
- result = instance._make_signal_handler_map()
- self.assertEqual(expected_result, result)
-
-
-try:
- FileNotFoundError
-except NameError:
- # Python 2 uses IOError.
- FileNotFoundError = functools.partial(IOError, errno.ENOENT)
-
-
-@mock.patch.object(os, "chdir")
-class change_working_directory_TestCase(scaffold.TestCase):
- """ Test cases for change_working_directory function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(change_working_directory_TestCase, self).setUp()
-
- self.test_directory = object()
- self.test_args = dict(
- directory=self.test_directory,
- )
-
- def test_changes_working_directory_to_specified_directory(
- self,
- mock_func_os_chdir):
- """ Should change working directory to specified directory. """
- args = self.test_args
- directory = self.test_directory
- daemon.daemon.change_working_directory(**args)
- mock_func_os_chdir.assert_called_with(directory)
-
- def test_raises_daemon_error_on_os_error(
- self,
- mock_func_os_chdir):
- """ Should raise a DaemonError on receiving an IOError. """
- args = self.test_args
- test_error = FileNotFoundError("No such directory")
- mock_func_os_chdir.side_effect = test_error
- expected_error = daemon.daemon.DaemonOSEnvironmentError
- exc = self.assertRaises(
- expected_error,
- daemon.daemon.change_working_directory, **args)
- self.assertEqual(test_error, exc.__cause__)
-
- def test_error_message_contains_original_error_message(
- self,
- mock_func_os_chdir):
- """ Should raise a DaemonError with original message. """
- args = self.test_args
- test_error = FileNotFoundError("No such directory")
- mock_func_os_chdir.side_effect = test_error
- expected_error = daemon.daemon.DaemonOSEnvironmentError
- exc = self.assertRaises(
- expected_error,
- daemon.daemon.change_working_directory, **args)
- self.assertIn(unicode(test_error), unicode(exc))
-
-
-@mock.patch.object(os, "chroot")
-@mock.patch.object(os, "chdir")
-class change_root_directory_TestCase(scaffold.TestCase):
- """ Test cases for change_root_directory function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(change_root_directory_TestCase, self).setUp()
-
- self.test_directory = object()
- self.test_args = dict(
- directory=self.test_directory,
- )
-
- def test_changes_working_directory_to_specified_directory(
- self,
- mock_func_os_chdir, mock_func_os_chroot):
- """ Should change working directory to specified directory. """
- args = self.test_args
- directory = self.test_directory
- daemon.daemon.change_root_directory(**args)
- mock_func_os_chdir.assert_called_with(directory)
-
- def test_changes_root_directory_to_specified_directory(
- self,
- mock_func_os_chdir, mock_func_os_chroot):
- """ Should change root directory to specified directory. """
- args = self.test_args
- directory = self.test_directory
- daemon.daemon.change_root_directory(**args)
- mock_func_os_chroot.assert_called_with(directory)
-
- def test_raises_daemon_error_on_os_error_from_chdir(
- self,
- mock_func_os_chdir, mock_func_os_chroot):
- """ Should raise a DaemonError on receiving an IOError from chdir. """
- args = self.test_args
- test_error = FileNotFoundError("No such directory")
- mock_func_os_chdir.side_effect = test_error
- expected_error = daemon.daemon.DaemonOSEnvironmentError
- exc = self.assertRaises(
- expected_error,
- daemon.daemon.change_root_directory, **args)
- self.assertEqual(test_error, exc.__cause__)
-
- def test_raises_daemon_error_on_os_error_from_chroot(
- self,
- mock_func_os_chdir, mock_func_os_chroot):
- """ Should raise a DaemonError on receiving an OSError from chroot. """
- args = self.test_args
- test_error = OSError(errno.EPERM, "No chroot for you!")
- mock_func_os_chroot.side_effect = test_error
- expected_error = daemon.daemon.DaemonOSEnvironmentError
- exc = self.assertRaises(
- expected_error,
- daemon.daemon.change_root_directory, **args)
- self.assertEqual(test_error, exc.__cause__)
-
- def test_error_message_contains_original_error_message(
- self,
- mock_func_os_chdir, mock_func_os_chroot):
- """ Should raise a DaemonError with original message. """
- args = self.test_args
- test_error = FileNotFoundError("No such directory")
- mock_func_os_chdir.side_effect = test_error
- expected_error = daemon.daemon.DaemonOSEnvironmentError
- exc = self.assertRaises(
- expected_error,
- daemon.daemon.change_root_directory, **args)
- self.assertIn(unicode(test_error), unicode(exc))
-
-
-@mock.patch.object(os, "umask")
-class change_file_creation_mask_TestCase(scaffold.TestCase):
- """ Test cases for change_file_creation_mask function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(change_file_creation_mask_TestCase, self).setUp()
-
- self.test_mask = object()
- self.test_args = dict(
- mask=self.test_mask,
- )
-
- def test_changes_umask_to_specified_mask(self, mock_func_os_umask):
- """ Should change working directory to specified directory. """
- args = self.test_args
- mask = self.test_mask
- daemon.daemon.change_file_creation_mask(**args)
- mock_func_os_umask.assert_called_with(mask)
-
- def test_raises_daemon_error_on_os_error_from_chdir(
- self,
- mock_func_os_umask):
- """ Should raise a DaemonError on receiving an OSError from umask. """
- args = self.test_args
- test_error = OSError(errno.EINVAL, "Whatchoo talkin' 'bout?")
- mock_func_os_umask.side_effect = test_error
- expected_error = daemon.daemon.DaemonOSEnvironmentError
- exc = self.assertRaises(
- expected_error,
- daemon.daemon.change_file_creation_mask, **args)
- self.assertEqual(test_error, exc.__cause__)
-
- def test_error_message_contains_original_error_message(
- self,
- mock_func_os_umask):
- """ Should raise a DaemonError with original message. """
- args = self.test_args
- test_error = FileNotFoundError("No such directory")
- mock_func_os_umask.side_effect = test_error
- expected_error = daemon.daemon.DaemonOSEnvironmentError
- exc = self.assertRaises(
- expected_error,
- daemon.daemon.change_file_creation_mask, **args)
- self.assertIn(unicode(test_error), unicode(exc))
-
-
-@mock.patch.object(os, "setgid")
-@mock.patch.object(os, "setuid")
-class change_process_owner_TestCase(scaffold.TestCase):
- """ Test cases for change_process_owner function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(change_process_owner_TestCase, self).setUp()
-
- self.test_uid = object()
- self.test_gid = object()
- self.test_args = dict(
- uid=self.test_uid,
- gid=self.test_gid,
- )
-
- def test_changes_gid_and_uid_in_order(
- self,
- mock_func_os_setuid, mock_func_os_setgid):
- """ Should change process GID and UID in correct order.
-
- Since the process requires appropriate privilege to use
- either of `setuid` or `setgid`, changing the UID must be
- done last.
-
- """
- args = self.test_args
- daemon.daemon.change_process_owner(**args)
- mock_func_os_setuid.assert_called()
- mock_func_os_setgid.assert_called()
-
- def test_changes_group_id_to_gid(
- self,
- mock_func_os_setuid, mock_func_os_setgid):
- """ Should change process GID to specified value. """
- args = self.test_args
- gid = self.test_gid
- daemon.daemon.change_process_owner(**args)
- mock_func_os_setgid.assert_called(gid)
-
- def test_changes_user_id_to_uid(
- self,
- mock_func_os_setuid, mock_func_os_setgid):
- """ Should change process UID to specified value. """
- args = self.test_args
- uid = self.test_uid
- daemon.daemon.change_process_owner(**args)
- mock_func_os_setuid.assert_called(uid)
-
- def test_raises_daemon_error_on_os_error_from_setgid(
- self,
- mock_func_os_setuid, mock_func_os_setgid):
- """ Should raise a DaemonError on receiving an OSError from setgid. """
- args = self.test_args
- test_error = OSError(errno.EPERM, "No switching for you!")
- mock_func_os_setgid.side_effect = test_error
- expected_error = daemon.daemon.DaemonOSEnvironmentError
- exc = self.assertRaises(
- expected_error,
- daemon.daemon.change_process_owner, **args)
- self.assertEqual(test_error, exc.__cause__)
-
- def test_raises_daemon_error_on_os_error_from_setuid(
- self,
- mock_func_os_setuid, mock_func_os_setgid):
- """ Should raise a DaemonError on receiving an OSError from setuid. """
- args = self.test_args
- test_error = OSError(errno.EPERM, "No switching for you!")
- mock_func_os_setuid.side_effect = test_error
- expected_error = daemon.daemon.DaemonOSEnvironmentError
- exc = self.assertRaises(
- expected_error,
- daemon.daemon.change_process_owner, **args)
- self.assertEqual(test_error, exc.__cause__)
-
- def test_error_message_contains_original_error_message(
- self,
- mock_func_os_setuid, mock_func_os_setgid):
- """ Should raise a DaemonError with original message. """
- args = self.test_args
- test_error = OSError(errno.EINVAL, "Whatchoo talkin' 'bout?")
- mock_func_os_setuid.side_effect = test_error
- expected_error = daemon.daemon.DaemonOSEnvironmentError
- exc = self.assertRaises(
- expected_error,
- daemon.daemon.change_process_owner, **args)
- self.assertIn(unicode(test_error), unicode(exc))
-
-
-RLimitResult = collections.namedtuple('RLimitResult', ['soft', 'hard'])
-
-fake_RLIMIT_CORE = object()
-
-@mock.patch.object(resource, "RLIMIT_CORE", new=fake_RLIMIT_CORE)
-@mock.patch.object(resource, "setrlimit", side_effect=(lambda x, y: None))
-@mock.patch.object(resource, "getrlimit", side_effect=(lambda x: None))
-class prevent_core_dump_TestCase(scaffold.TestCase):
- """ Test cases for prevent_core_dump function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(prevent_core_dump_TestCase, self).setUp()
-
- def test_sets_core_limit_to_zero(
- self,
- mock_func_resource_getrlimit, mock_func_resource_setrlimit):
- """ Should set the RLIMIT_CORE resource to zero. """
- expected_resource = fake_RLIMIT_CORE
- expected_limit = tuple(RLimitResult(soft=0, hard=0))
- daemon.daemon.prevent_core_dump()
- mock_func_resource_getrlimit.assert_called_with(expected_resource)
- mock_func_resource_setrlimit.assert_called_with(
- expected_resource, expected_limit)
-
- def test_raises_error_when_no_core_resource(
- self,
- mock_func_resource_getrlimit, mock_func_resource_setrlimit):
- """ Should raise DaemonError if no RLIMIT_CORE resource. """
- test_error = ValueError("Bogus platform doesn't have RLIMIT_CORE")
- def fake_getrlimit(res):
- if res == resource.RLIMIT_CORE:
- raise test_error
- else:
- return None
- mock_func_resource_getrlimit.side_effect = fake_getrlimit
- expected_error = daemon.daemon.DaemonOSEnvironmentError
- exc = self.assertRaises(
- expected_error,
- daemon.daemon.prevent_core_dump)
- self.assertEqual(test_error, exc.__cause__)
-
-
-@mock.patch.object(os, "close")
-class close_file_descriptor_if_open_TestCase(scaffold.TestCase):
- """ Test cases for close_file_descriptor_if_open function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(close_file_descriptor_if_open_TestCase, self).setUp()
-
- self.fake_fd = 274
-
- def test_requests_file_descriptor_close(self, mock_func_os_close):
- """ Should request close of file descriptor. """
- fd = self.fake_fd
- daemon.daemon.close_file_descriptor_if_open(fd)
- mock_func_os_close.assert_called_with(fd)
-
- def test_ignores_badfd_error_on_close(self, mock_func_os_close):
- """ Should ignore OSError EBADF when closing. """
- fd = self.fake_fd
- test_error = OSError(errno.EBADF, "Bad file descriptor")
- def fake_os_close(fd):
- raise test_error
- mock_func_os_close.side_effect = fake_os_close
- daemon.daemon.close_file_descriptor_if_open(fd)
- mock_func_os_close.assert_called_with(fd)
-
- def test_raises_error_if_oserror_on_close(self, mock_func_os_close):
- """ Should raise DaemonError if an OSError occurs when closing. """
- fd = self.fake_fd
- test_error = OSError(object(), "Unexpected error")
- def fake_os_close(fd):
- raise test_error
- mock_func_os_close.side_effect = fake_os_close
- expected_error = daemon.daemon.DaemonOSEnvironmentError
- exc = self.assertRaises(
- expected_error,
- daemon.daemon.close_file_descriptor_if_open, fd)
- self.assertEqual(test_error, exc.__cause__)
-
- def test_raises_error_if_ioerror_on_close(self, mock_func_os_close):
- """ Should raise DaemonError if an IOError occurs when closing. """
- fd = self.fake_fd
- test_error = IOError(object(), "Unexpected error")
- def fake_os_close(fd):
- raise test_error
- mock_func_os_close.side_effect = fake_os_close
- expected_error = daemon.daemon.DaemonOSEnvironmentError
- exc = self.assertRaises(
- expected_error,
- daemon.daemon.close_file_descriptor_if_open, fd)
- self.assertEqual(test_error, exc.__cause__)
-
-
-class maxfd_TestCase(scaffold.TestCase):
- """ Test cases for module MAXFD constant. """
-
- def test_positive(self):
- """ Should be a positive number. """
- maxfd = daemon.daemon.MAXFD
- self.assertTrue(maxfd > 0)
-
- def test_integer(self):
- """ Should be an integer. """
- maxfd = daemon.daemon.MAXFD
- self.assertEqual(int(maxfd), maxfd)
-
- def test_reasonably_high(self):
- """ Should be reasonably high for default open files limit.
-
- If the system reports a limit of “infinity” on maximum
- file descriptors, we still need a finite number in order
- to close “all” of them. Ensure this is reasonably high
- to catch most use cases.
-
- """
- expected_minimum = 2048
- maxfd = daemon.daemon.MAXFD
- self.assertTrue(
- expected_minimum <= maxfd,
- msg=(
- "MAXFD should be at least {minimum!r}"
- " (got {maxfd!r})".format(
- minimum=expected_minimum, maxfd=maxfd)))
-
-
-fake_default_maxfd = 8
-fake_RLIMIT_NOFILE = object()
-fake_RLIM_INFINITY = object()
-fake_rlimit_nofile_large = 2468
-
-def fake_getrlimit_nofile_soft_infinity(resource):
- result = RLimitResult(soft=fake_RLIM_INFINITY, hard=object())
- if resource != fake_RLIMIT_NOFILE:
- result = NotImplemented
- return result
-
-def fake_getrlimit_nofile_hard_infinity(resource):
- result = RLimitResult(soft=object(), hard=fake_RLIM_INFINITY)
- if resource != fake_RLIMIT_NOFILE:
- result = NotImplemented
- return result
-
-def fake_getrlimit_nofile_hard_large(resource):
- result = RLimitResult(soft=object(), hard=fake_rlimit_nofile_large)
- if resource != fake_RLIMIT_NOFILE:
- result = NotImplemented
- return result
-
-@mock.patch.object(daemon.daemon, "MAXFD", new=fake_default_maxfd)
-@mock.patch.object(resource, "RLIMIT_NOFILE", new=fake_RLIMIT_NOFILE)
-@mock.patch.object(resource, "RLIM_INFINITY", new=fake_RLIM_INFINITY)
-@mock.patch.object(
- resource, "getrlimit",
- side_effect=fake_getrlimit_nofile_hard_large)
-class get_maximum_file_descriptors_TestCase(scaffold.TestCase):
- """ Test cases for get_maximum_file_descriptors function. """
-
- def test_returns_system_hard_limit(self, mock_func_resource_getrlimit):
- """ Should return process hard limit on number of files. """
- expected_result = fake_rlimit_nofile_large
- result = daemon.daemon.get_maximum_file_descriptors()
- self.assertEqual(expected_result, result)
-
- def test_returns_module_default_if_hard_limit_infinity(
- self, mock_func_resource_getrlimit):
- """ Should return module MAXFD if hard limit is infinity. """
- mock_func_resource_getrlimit.side_effect = (
- fake_getrlimit_nofile_hard_infinity)
- expected_result = fake_default_maxfd
- result = daemon.daemon.get_maximum_file_descriptors()
- self.assertEqual(expected_result, result)
-
-
-def fake_get_maximum_file_descriptors():
- return fake_default_maxfd
-
-@mock.patch.object(resource, "RLIMIT_NOFILE", new=fake_RLIMIT_NOFILE)
-@mock.patch.object(resource, "RLIM_INFINITY", new=fake_RLIM_INFINITY)
-@mock.patch.object(
- resource, "getrlimit",
- new=fake_getrlimit_nofile_soft_infinity)
-@mock.patch.object(
- daemon.daemon, "get_maximum_file_descriptors",
- new=fake_get_maximum_file_descriptors)
-@mock.patch.object(daemon.daemon, "close_file_descriptor_if_open")
-class close_all_open_files_TestCase(scaffold.TestCase):
- """ Test cases for close_all_open_files function. """
-
- def test_requests_all_open_files_to_close(
- self, mock_func_close_file_descriptor_if_open):
- """ Should request close of all open files. """
- expected_file_descriptors = range(fake_default_maxfd)
- expected_calls = [
- mock.call(fd) for fd in expected_file_descriptors]
- daemon.daemon.close_all_open_files()
- mock_func_close_file_descriptor_if_open.assert_has_calls(
- expected_calls, any_order=True)
-
- def test_requests_all_but_excluded_files_to_close(
- self, mock_func_close_file_descriptor_if_open):
- """ Should request close of all open files but those excluded. """
- test_exclude = set([3, 7])
- args = dict(
- exclude=test_exclude,
- )
- expected_file_descriptors = set(
- fd for fd in range(fake_default_maxfd)
- if fd not in test_exclude)
- expected_calls = [
- mock.call(fd) for fd in expected_file_descriptors]
- daemon.daemon.close_all_open_files(**args)
- mock_func_close_file_descriptor_if_open.assert_has_calls(
- expected_calls, any_order=True)
-
-
-class detach_process_context_TestCase(scaffold.TestCase):
- """ Test cases for detach_process_context function. """
-
- class FakeOSExit(SystemExit):
- """ Fake exception raised for os._exit(). """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(detach_process_context_TestCase, self).setUp()
-
- self.mock_module_os = mock.MagicMock(wraps=os)
-
- fake_pids = [0, 0]
- func_patcher_os_fork = mock.patch.object(
- os, "fork",
- side_effect=iter(fake_pids))
- self.mock_func_os_fork = func_patcher_os_fork.start()
- self.addCleanup(func_patcher_os_fork.stop)
- self.mock_module_os.attach_mock(self.mock_func_os_fork, "fork")
-
- func_patcher_os_setsid = mock.patch.object(os, "setsid")
- self.mock_func_os_setsid = func_patcher_os_setsid.start()
- self.addCleanup(func_patcher_os_setsid.stop)
- self.mock_module_os.attach_mock(self.mock_func_os_setsid, "setsid")
-
- def raise_os_exit(status=None):
- raise self.FakeOSExit(status)
-
- func_patcher_os_force_exit = mock.patch.object(
- os, "_exit",
- side_effect=raise_os_exit)
- self.mock_func_os_force_exit = func_patcher_os_force_exit.start()
- self.addCleanup(func_patcher_os_force_exit.stop)
- self.mock_module_os.attach_mock(self.mock_func_os_force_exit, "_exit")
-
- def test_parent_exits(self):
- """ Parent process should exit. """
- parent_pid = 23
- self.mock_func_os_fork.side_effect = iter([parent_pid])
- self.assertRaises(
- self.FakeOSExit,
- daemon.daemon.detach_process_context)
- self.mock_module_os.assert_has_calls([
- mock.call.fork(),
- mock.call._exit(0),
- ])
-
- def test_first_fork_error_raises_error(self):
- """ Error on first fork should raise DaemonProcessDetachError. """
- fork_errno = 13
- fork_strerror = "Bad stuff happened"
- test_error = OSError(fork_errno, fork_strerror)
- test_pids_iter = iter([test_error])
-
- def fake_fork():
- next_item = next(test_pids_iter)
- if isinstance(next_item, Exception):
- raise next_item
- else:
- return next_item
-
- self.mock_func_os_fork.side_effect = fake_fork
- exc = self.assertRaises(
- daemon.daemon.DaemonProcessDetachError,
- daemon.daemon.detach_process_context)
- self.assertEqual(test_error, exc.__cause__)
- self.mock_module_os.assert_has_calls([
- mock.call.fork(),
- ])
-
- def test_child_starts_new_process_group(self):
- """ Child should start new process group. """
- daemon.daemon.detach_process_context()
- self.mock_module_os.assert_has_calls([
- mock.call.fork(),
- mock.call.setsid(),
- ])
-
- def test_child_forks_next_parent_exits(self):
- """ Child should fork, then exit if parent. """
- fake_pids = [0, 42]
- self.mock_func_os_fork.side_effect = iter(fake_pids)
- self.assertRaises(
- self.FakeOSExit,
- daemon.daemon.detach_process_context)
- self.mock_module_os.assert_has_calls([
- mock.call.fork(),
- mock.call.setsid(),
- mock.call.fork(),
- mock.call._exit(0),
- ])
-
- def test_second_fork_error_reports_to_stderr(self):
- """ Error on second fork should cause report to stderr. """
- fork_errno = 17
- fork_strerror = "Nasty stuff happened"
- test_error = OSError(fork_errno, fork_strerror)
- test_pids_iter = iter([0, test_error])
-
- def fake_fork():
- next_item = next(test_pids_iter)
- if isinstance(next_item, Exception):
- raise next_item
- else:
- return next_item
-
- self.mock_func_os_fork.side_effect = fake_fork
- exc = self.assertRaises(
- daemon.daemon.DaemonProcessDetachError,
- daemon.daemon.detach_process_context)
- self.assertEqual(test_error, exc.__cause__)
- self.mock_module_os.assert_has_calls([
- mock.call.fork(),
- mock.call.setsid(),
- mock.call.fork(),
- ])
-
- def test_child_forks_next_child_continues(self):
- """ Child should fork, then continue if child. """
- daemon.daemon.detach_process_context()
- self.mock_module_os.assert_has_calls([
- mock.call.fork(),
- mock.call.setsid(),
- mock.call.fork(),
- ])
-
-
-@mock.patch("os.getppid", return_value=765)
-class is_process_started_by_init_TestCase(scaffold.TestCase):
- """ Test cases for is_process_started_by_init function. """
-
- def test_returns_false_by_default(self, mock_func_os_getppid):
- """ Should return False under normal circumstances. """
- expected_result = False
- result = daemon.daemon.is_process_started_by_init()
- self.assertIs(result, expected_result)
-
- def test_returns_true_if_parent_process_is_init(
- self, mock_func_os_getppid):
- """ Should return True if parent process is `init`. """
- init_pid = 1
- mock_func_os_getppid.return_value = init_pid
- expected_result = True
- result = daemon.daemon.is_process_started_by_init()
- self.assertIs(result, expected_result)
-
-
-class is_socket_TestCase(scaffold.TestCase):
- """ Test cases for is_socket function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(is_socket_TestCase, self).setUp()
-
- def fake_getsockopt(level, optname, buflen=None):
- result = object()
- if optname is socket.SO_TYPE:
- result = socket.SOCK_RAW
- return result
-
- self.fake_socket_getsockopt_func = fake_getsockopt
-
- self.fake_socket_error = socket.error(
- errno.ENOTSOCK,
- "Socket operation on non-socket")
-
- self.mock_socket = mock.MagicMock(spec=socket.socket)
- self.mock_socket.getsockopt.side_effect = self.fake_socket_error
-
- def fake_socket_fromfd(fd, family, type, proto=None):
- return self.mock_socket
-
- func_patcher_socket_fromfd = mock.patch.object(
- socket, "fromfd",
- side_effect=fake_socket_fromfd)
- func_patcher_socket_fromfd.start()
- self.addCleanup(func_patcher_socket_fromfd.stop)
-
- def test_returns_false_by_default(self):
- """ Should return False under normal circumstances. """
- test_fd = 23
- expected_result = False
- result = daemon.daemon.is_socket(test_fd)
- self.assertIs(result, expected_result)
-
- def test_returns_true_if_stdin_is_socket(self):
- """ Should return True if `stdin` is a socket. """
- test_fd = 23
- getsockopt = self.mock_socket.getsockopt
- getsockopt.side_effect = self.fake_socket_getsockopt_func
- expected_result = True
- result = daemon.daemon.is_socket(test_fd)
- self.assertIs(result, expected_result)
-
- def test_returns_false_if_stdin_socket_raises_error(self):
- """ Should return True if `stdin` is a socket and raises error. """
- test_fd = 23
- getsockopt = self.mock_socket.getsockopt
- getsockopt.side_effect = socket.error(
- object(), "Weird socket stuff")
- expected_result = True
- result = daemon.daemon.is_socket(test_fd)
- self.assertIs(result, expected_result)
-
-
-class is_process_started_by_superserver_TestCase(scaffold.TestCase):
- """ Test cases for is_process_started_by_superserver function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(is_process_started_by_superserver_TestCase, self).setUp()
-
- def fake_is_socket(fd):
- if sys.__stdin__.fileno() == fd:
- result = self.fake_stdin_is_socket_func()
- else:
- result = False
- return result
-
- self.fake_stdin_is_socket_func = (lambda: False)
-
- func_patcher_is_socket = mock.patch.object(
- daemon.daemon, "is_socket",
- side_effect=fake_is_socket)
- func_patcher_is_socket.start()
- self.addCleanup(func_patcher_is_socket.stop)
-
- def test_returns_false_by_default(self):
- """ Should return False under normal circumstances. """
- expected_result = False
- result = daemon.daemon.is_process_started_by_superserver()
- self.assertIs(result, expected_result)
-
- def test_returns_true_if_stdin_is_socket(self):
- """ Should return True if `stdin` is a socket. """
- self.fake_stdin_is_socket_func = (lambda: True)
- expected_result = True
- result = daemon.daemon.is_process_started_by_superserver()
- self.assertIs(result, expected_result)
-
-
-@mock.patch.object(
- daemon.daemon, "is_process_started_by_superserver",
- return_value=False)
-@mock.patch.object(
- daemon.daemon, "is_process_started_by_init",
- return_value=False)
-class is_detach_process_context_required_TestCase(scaffold.TestCase):
- """ Test cases for is_detach_process_context_required function. """
-
- def test_returns_true_by_default(
- self,
- mock_func_is_process_started_by_init,
- mock_func_is_process_started_by_superserver):
- """ Should return True under normal circumstances. """
- expected_result = True
- result = daemon.daemon.is_detach_process_context_required()
- self.assertIs(result, expected_result)
-
- def test_returns_false_if_started_by_init(
- self,
- mock_func_is_process_started_by_init,
- mock_func_is_process_started_by_superserver):
- """ Should return False if current process started by init. """
- mock_func_is_process_started_by_init.return_value = True
- expected_result = False
- result = daemon.daemon.is_detach_process_context_required()
- self.assertIs(result, expected_result)
-
- def test_returns_true_if_started_by_superserver(
- self,
- mock_func_is_process_started_by_init,
- mock_func_is_process_started_by_superserver):
- """ Should return False if current process started by superserver. """
- mock_func_is_process_started_by_superserver.return_value = True
- expected_result = False
- result = daemon.daemon.is_detach_process_context_required()
- self.assertIs(result, expected_result)
-
-
-def setup_streams_fixtures(testcase):
- """ Set up common test fixtures for standard streams. """
- testcase.stream_file_paths = dict(
- stdin=tempfile.mktemp(),
- stdout=tempfile.mktemp(),
- stderr=tempfile.mktemp(),
- )
-
- testcase.stream_files_by_name = dict(
- (name, FakeFileDescriptorStringIO())
- for name in ['stdin', 'stdout', 'stderr']
- )
-
- testcase.stream_files_by_path = dict(
- (testcase.stream_file_paths[name],
- testcase.stream_files_by_name[name])
- for name in ['stdin', 'stdout', 'stderr']
- )
-
-
-@mock.patch.object(os, "dup2")
-class redirect_stream_TestCase(scaffold.TestCase):
- """ Test cases for redirect_stream function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(redirect_stream_TestCase, self).setUp()
-
- self.test_system_stream = FakeFileDescriptorStringIO()
- self.test_target_stream = FakeFileDescriptorStringIO()
- self.test_null_file = FakeFileDescriptorStringIO()
-
- def fake_os_open(path, flag, mode=None):
- if path == os.devnull:
- result = self.test_null_file.fileno()
- else:
- raise FileNotFoundError("No such file", path)
- return result
-
- func_patcher_os_open = mock.patch.object(
- os, "open",
- side_effect=fake_os_open)
- self.mock_func_os_open = func_patcher_os_open.start()
- self.addCleanup(func_patcher_os_open.stop)
-
- def test_duplicates_target_file_descriptor(
- self, mock_func_os_dup2):
- """ Should duplicate file descriptor from target to system stream. """
- system_stream = self.test_system_stream
- system_fileno = system_stream.fileno()
- target_stream = self.test_target_stream
- target_fileno = target_stream.fileno()
- daemon.daemon.redirect_stream(system_stream, target_stream)
- mock_func_os_dup2.assert_called_with(target_fileno, system_fileno)
-
- def test_duplicates_null_file_descriptor_by_default(
- self, mock_func_os_dup2):
- """ Should by default duplicate the null file to the system stream. """
- system_stream = self.test_system_stream
- system_fileno = system_stream.fileno()
- target_stream = None
- null_path = os.devnull
- null_flag = os.O_RDWR
- null_file = self.test_null_file
- null_fileno = null_file.fileno()
- daemon.daemon.redirect_stream(system_stream, target_stream)
- self.mock_func_os_open.assert_called_with(null_path, null_flag)
- mock_func_os_dup2.assert_called_with(null_fileno, system_fileno)
-
-
-class make_default_signal_map_TestCase(scaffold.TestCase):
- """ Test cases for make_default_signal_map function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(make_default_signal_map_TestCase, self).setUp()
-
- # Use whatever default string type this Python version needs.
- signal_module_name = str('signal')
- self.fake_signal_module = ModuleType(signal_module_name)
-
- fake_signal_names = [
- 'SIGHUP',
- 'SIGCLD',
- 'SIGSEGV',
- 'SIGTSTP',
- 'SIGTTIN',
- 'SIGTTOU',
- 'SIGTERM',
- ]
- for name in fake_signal_names:
- setattr(self.fake_signal_module, name, object())
-
- module_patcher_signal = mock.patch.object(
- daemon.daemon, "signal", new=self.fake_signal_module)
- module_patcher_signal.start()
- self.addCleanup(module_patcher_signal.stop)
-
- default_signal_map_by_name = {
- 'SIGTSTP': None,
- 'SIGTTIN': None,
- 'SIGTTOU': None,
- 'SIGTERM': 'terminate',
- }
- self.default_signal_map = dict(
- (getattr(self.fake_signal_module, name), target)
- for (name, target) in default_signal_map_by_name.items())
-
- def test_returns_constructed_signal_map(self):
- """ Should return map per default. """
- expected_result = self.default_signal_map
- result = daemon.daemon.make_default_signal_map()
- self.assertEqual(expected_result, result)
-
- def test_returns_signal_map_with_only_ids_in_signal_module(self):
- """ Should return map with only signals in the `signal` module.
-
- The `signal` module is documented to only define those
- signals which exist on the running system. Therefore the
- default map should not contain any signals which are not
- defined in the `signal` module.
-
- """
- del(self.default_signal_map[self.fake_signal_module.SIGTTOU])
- del(self.fake_signal_module.SIGTTOU)
- expected_result = self.default_signal_map
- result = daemon.daemon.make_default_signal_map()
- self.assertEqual(expected_result, result)
-
-
-@mock.patch.object(daemon.daemon.signal, "signal")
-class set_signal_handlers_TestCase(scaffold.TestCase):
- """ Test cases for set_signal_handlers function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(set_signal_handlers_TestCase, self).setUp()
-
- self.signal_handler_map = {
- signal.SIGQUIT: object(),
- signal.SIGSEGV: object(),
- signal.SIGINT: object(),
- }
-
- def test_sets_signal_handler_for_each_item(self, mock_func_signal_signal):
- """ Should set signal handler for each item in map. """
- signal_handler_map = self.signal_handler_map
- expected_calls = [
- mock.call(signal_number, handler)
- for (signal_number, handler) in signal_handler_map.items()]
- daemon.daemon.set_signal_handlers(signal_handler_map)
- self.assertEquals(expected_calls, mock_func_signal_signal.mock_calls)
-
-
-@mock.patch.object(daemon.daemon.atexit, "register")
-class register_atexit_function_TestCase(scaffold.TestCase):
- """ Test cases for register_atexit_function function. """
-
- def test_registers_function_for_atexit_processing(
- self, mock_func_atexit_register):
- """ Should register specified function for atexit processing. """
- func = object()
- daemon.daemon.register_atexit_function(func)
- mock_func_atexit_register.assert_called_with(func)
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/external_libs/python/python-daemon-2.0.5/test/test_metadata.py b/external_libs/python/python-daemon-2.0.5/test/test_metadata.py
deleted file mode 100644
index 692753f4..00000000
--- a/external_libs/python/python-daemon-2.0.5/test/test_metadata.py
+++ /dev/null
@@ -1,380 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# test/test_metadata.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the Apache License, version 2.0 as published by the
-# Apache Software Foundation.
-# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-""" Unit test for ‘_metadata’ private module.
- """
-
-from __future__ import (absolute_import, unicode_literals)
-
-import sys
-import errno
-import re
-try:
- # Python 3 standard library.
- import urllib.parse as urlparse
-except ImportError:
- # Python 2 standard library.
- import urlparse
-import functools
-import collections
-import json
-
-import pkg_resources
-import mock
-import testtools.helpers
-import testtools.matchers
-import testscenarios
-
-from . import scaffold
-from .scaffold import (basestring, unicode)
-
-import daemon._metadata as metadata
-
-
-class HasAttribute(testtools.matchers.Matcher):
- """ A matcher to assert an object has a named attribute. """
-
- def __init__(self, name):
- self.attribute_name = name
-
- def match(self, instance):
- """ Assert the object `instance` has an attribute named `name`. """
- result = None
- if not testtools.helpers.safe_hasattr(instance, self.attribute_name):
- result = AttributeNotFoundMismatch(instance, self.attribute_name)
- return result
-
-
-class AttributeNotFoundMismatch(testtools.matchers.Mismatch):
- """ The specified instance does not have the named attribute. """
-
- def __init__(self, instance, name):
- self.instance = instance
- self.attribute_name = name
-
- def describe(self):
- """ Emit a text description of this mismatch. """
- text = (
- "{instance!r}"
- " has no attribute named {name!r}").format(
- instance=self.instance, name=self.attribute_name)
- return text
-
-
-class metadata_value_TestCase(scaffold.TestCaseWithScenarios):
- """ Test cases for metadata module values. """
-
- expected_str_attributes = set([
- 'version_installed',
- 'author',
- 'copyright',
- 'license',
- 'url',
- ])
-
- scenarios = [
- (name, {'attribute_name': name})
- for name in expected_str_attributes]
- for (name, params) in scenarios:
- if name == 'version_installed':
- # No duck typing, this attribute might be None.
- params['ducktype_attribute_name'] = NotImplemented
- continue
- # Expect an attribute of ‘str’ to test this value.
- params['ducktype_attribute_name'] = 'isdigit'
-
- def test_module_has_attribute(self):
- """ Metadata should have expected value as a module attribute. """
- self.assertThat(
- metadata, HasAttribute(self.attribute_name))
-
- def test_module_attribute_has_duck_type(self):
- """ Metadata value should have expected duck-typing attribute. """
- if self.ducktype_attribute_name == NotImplemented:
- self.skipTest("Can't assert this attribute's type")
- instance = getattr(metadata, self.attribute_name)
- self.assertThat(
- instance, HasAttribute(self.ducktype_attribute_name))
-
-
-class parse_person_field_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘get_latest_version’ function. """
-
- scenarios = [
- ('simple', {
- 'test_person': "Foo Bar <foo.bar@example.com>",
- 'expected_result': ("Foo Bar", "foo.bar@example.com"),
- }),
- ('empty', {
- 'test_person': "",
- 'expected_result': (None, None),
- }),
- ('none', {
- 'test_person': None,
- 'expected_error': TypeError,
- }),
- ('no email', {
- 'test_person': "Foo Bar",
- 'expected_result': ("Foo Bar", None),
- }),
- ]
-
- def test_returns_expected_result(self):
- """ Should return expected result. """
- if hasattr(self, 'expected_error'):
- self.assertRaises(
- self.expected_error,
- metadata.parse_person_field, self.test_person)
- else:
- result = metadata.parse_person_field(self.test_person)
- self.assertEqual(self.expected_result, result)
-
-
-class YearRange_TestCase(scaffold.TestCaseWithScenarios):
- """ Test cases for ‘YearRange’ class. """
-
- scenarios = [
- ('simple', {
- 'begin_year': 1970,
- 'end_year': 1979,
- 'expected_text': "1970–1979",
- }),
- ('same year', {
- 'begin_year': 1970,
- 'end_year': 1970,
- 'expected_text': "1970",
- }),
- ('no end year', {
- 'begin_year': 1970,
- 'end_year': None,
- 'expected_text': "1970",
- }),
- ]
-
- def setUp(self):
- """ Set up test fixtures. """
- super(YearRange_TestCase, self).setUp()
-
- self.test_instance = metadata.YearRange(
- self.begin_year, self.end_year)
-
- def test_text_representation_as_expected(self):
- """ Text representation should be as expected. """
- result = unicode(self.test_instance)
- self.assertEqual(result, self.expected_text)
-
-
-FakeYearRange = collections.namedtuple('FakeYearRange', ['begin', 'end'])
-
-@mock.patch.object(metadata, 'YearRange', new=FakeYearRange)
-class make_year_range_TestCase(scaffold.TestCaseWithScenarios):
- """ Test cases for ‘make_year_range’ function. """
-
- scenarios = [
- ('simple', {
- 'begin_year': "1970",
- 'end_date': "1979-01-01",
- 'expected_range': FakeYearRange(begin=1970, end=1979),
- }),
- ('same year', {
- 'begin_year': "1970",
- 'end_date': "1970-01-01",
- 'expected_range': FakeYearRange(begin=1970, end=1970),
- }),
- ('no end year', {
- 'begin_year': "1970",
- 'end_date': None,
- 'expected_range': FakeYearRange(begin=1970, end=None),
- }),
- ('end date UNKNOWN token', {
- 'begin_year': "1970",
- 'end_date': "UNKNOWN",
- 'expected_range': FakeYearRange(begin=1970, end=None),
- }),
- ('end date FUTURE token', {
- 'begin_year': "1970",
- 'end_date': "FUTURE",
- 'expected_range': FakeYearRange(begin=1970, end=None),
- }),
- ]
-
- def test_result_matches_expected_range(self):
- """ Result should match expected YearRange. """
- result = metadata.make_year_range(self.begin_year, self.end_date)
- self.assertEqual(result, self.expected_range)
-
-
-class metadata_content_TestCase(scaffold.TestCase):
- """ Test cases for content of metadata. """
-
- def test_copyright_formatted_correctly(self):
- """ Copyright statement should be formatted correctly. """
- regex_pattern = (
- "Copyright © "
- "\d{4}" # four-digit year
- "(?:–\d{4})?" # optional range dash and ending four-digit year
- )
- regex_flags = re.UNICODE
- self.assertThat(
- metadata.copyright,
- testtools.matchers.MatchesRegex(regex_pattern, regex_flags))
-
- def test_author_formatted_correctly(self):
- """ Author information should be formatted correctly. """
- regex_pattern = (
- ".+ " # name
- "<[^>]+>" # email address, in angle brackets
- )
- regex_flags = re.UNICODE
- self.assertThat(
- metadata.author,
- testtools.matchers.MatchesRegex(regex_pattern, regex_flags))
-
- def test_copyright_contains_author(self):
- """ Copyright information should contain author information. """
- self.assertThat(
- metadata.copyright,
- testtools.matchers.Contains(metadata.author))
-
- def test_url_parses_correctly(self):
- """ Homepage URL should parse correctly. """
- result = urlparse.urlparse(metadata.url)
- self.assertIsInstance(
- result, urlparse.ParseResult,
- "URL value {url!r} did not parse correctly".format(
- url=metadata.url))
-
-
-try:
- FileNotFoundError
-except NameError:
- # Python 2 uses IOError.
- FileNotFoundError = functools.partial(IOError, errno.ENOENT)
-
-version_info_filename = "version_info.json"
-
-def fake_func_has_metadata(testcase, resource_name):
- """ Fake the behaviour of ‘pkg_resources.Distribution.has_metadata’. """
- if (
- resource_name != testcase.expected_resource_name
- or not hasattr(testcase, 'test_version_info')):
- return False
- return True
-
-
-def fake_func_get_metadata(testcase, resource_name):
- """ Fake the behaviour of ‘pkg_resources.Distribution.get_metadata’. """
- if not fake_func_has_metadata(testcase, resource_name):
- error = FileNotFoundError(resource_name)
- raise error
- content = testcase.test_version_info
- return content
-
-
-def fake_func_get_distribution(testcase, distribution_name):
- """ Fake the behaviour of ‘pkg_resources.get_distribution’. """
- if distribution_name != metadata.distribution_name:
- raise pkg_resources.DistributionNotFound
- if hasattr(testcase, 'get_distribution_error'):
- raise testcase.get_distribution_error
- mock_distribution = testcase.mock_distribution
- mock_distribution.has_metadata.side_effect = functools.partial(
- fake_func_has_metadata, testcase)
- mock_distribution.get_metadata.side_effect = functools.partial(
- fake_func_get_metadata, testcase)
- return mock_distribution
-
-
-@mock.patch.object(metadata, 'distribution_name', new="mock-dist")
-class get_distribution_version_info_TestCase(scaffold.TestCaseWithScenarios):
- """ Test cases for ‘get_distribution_version_info’ function. """
-
- default_version_info = {
- 'release_date': "UNKNOWN",
- 'version': "UNKNOWN",
- 'maintainer': "UNKNOWN",
- }
-
- scenarios = [
- ('version 0.0', {
- 'test_version_info': json.dumps({
- 'version': "0.0",
- }),
- 'expected_version_info': {'version': "0.0"},
- }),
- ('version 1.0', {
- 'test_version_info': json.dumps({
- 'version': "1.0",
- }),
- 'expected_version_info': {'version': "1.0"},
- }),
- ('file lorem_ipsum.json', {
- 'version_info_filename': "lorem_ipsum.json",
- 'test_version_info': json.dumps({
- 'version': "1.0",
- }),
- 'expected_version_info': {'version': "1.0"},
- }),
- ('not installed', {
- 'get_distribution_error': pkg_resources.DistributionNotFound(),
- 'expected_version_info': default_version_info,
- }),
- ('no version_info', {
- 'expected_version_info': default_version_info,
- }),
- ]
-
- def setUp(self):
- """ Set up test fixtures. """
- super(get_distribution_version_info_TestCase, self).setUp()
-
- if hasattr(self, 'expected_resource_name'):
- self.test_args = {'filename': self.expected_resource_name}
- else:
- self.test_args = {}
- self.expected_resource_name = version_info_filename
-
- self.mock_distribution = mock.MagicMock()
- func_patcher_get_distribution = mock.patch.object(
- pkg_resources, 'get_distribution')
- func_patcher_get_distribution.start()
- self.addCleanup(func_patcher_get_distribution.stop)
- pkg_resources.get_distribution.side_effect = functools.partial(
- fake_func_get_distribution, self)
-
- def test_requests_installed_distribution(self):
- """ The package distribution should be retrieved. """
- expected_distribution_name = metadata.distribution_name
- version_info = metadata.get_distribution_version_info(**self.test_args)
- pkg_resources.get_distribution.assert_called_with(
- expected_distribution_name)
-
- def test_requests_specified_filename(self):
- """ The specified metadata resource name should be requested. """
- if hasattr(self, 'get_distribution_error'):
- self.skipTest("No access to distribution")
- version_info = metadata.get_distribution_version_info(**self.test_args)
- self.mock_distribution.has_metadata.assert_called_with(
- self.expected_resource_name)
-
- def test_result_matches_expected_items(self):
- """ The result should match the expected items. """
- version_info = metadata.get_distribution_version_info(**self.test_args)
- self.assertEqual(self.expected_version_info, version_info)
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/external_libs/python/python-daemon-2.0.5/test/test_pidfile.py b/external_libs/python/python-daemon-2.0.5/test/test_pidfile.py
deleted file mode 100644
index 9b636ec8..00000000
--- a/external_libs/python/python-daemon-2.0.5/test/test_pidfile.py
+++ /dev/null
@@ -1,472 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# test/test_pidfile.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the Apache License, version 2.0 as published by the
-# Apache Software Foundation.
-# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-""" Unit test for ‘pidfile’ module.
- """
-
-from __future__ import (absolute_import, unicode_literals)
-
-try:
- # Python 3 standard library.
- import builtins
-except ImportError:
- # Python 2 standard library.
- import __builtin__ as builtins
-import os
-import itertools
-import tempfile
-import errno
-import functools
-try:
- # Standard library of Python 2.7 and later.
- from io import StringIO
-except ImportError:
- # Standard library of Python 2.6 and earlier.
- from StringIO import StringIO
-
-import mock
-import lockfile
-
-from . import scaffold
-
-import daemon.pidfile
-
-
-class FakeFileDescriptorStringIO(StringIO, object):
- """ A StringIO class that fakes a file descriptor. """
-
- _fileno_generator = itertools.count()
-
- def __init__(self, *args, **kwargs):
- self._fileno = next(self._fileno_generator)
- super(FakeFileDescriptorStringIO, self).__init__(*args, **kwargs)
-
- def fileno(self):
- return self._fileno
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- pass
-
-
-try:
- FileNotFoundError
- PermissionError
-except NameError:
- # Python 2 uses IOError.
- FileNotFoundError = functools.partial(IOError, errno.ENOENT)
- PermissionError = functools.partial(IOError, errno.EPERM)
-
-
-def make_pidlockfile_scenarios():
- """ Make a collection of scenarios for testing `PIDLockFile` instances.
-
- :return: A collection of scenarios for tests involving
- `PIDLockfFile` instances.
-
- The collection is a mapping from scenario name to a dictionary of
- scenario attributes.
-
- """
-
- fake_current_pid = 235
- fake_other_pid = 8642
- fake_pidfile_path = tempfile.mktemp()
-
- fake_pidfile_empty = FakeFileDescriptorStringIO()
- fake_pidfile_current_pid = FakeFileDescriptorStringIO(
- "{pid:d}\n".format(pid=fake_current_pid))
- fake_pidfile_other_pid = FakeFileDescriptorStringIO(
- "{pid:d}\n".format(pid=fake_other_pid))
- fake_pidfile_bogus = FakeFileDescriptorStringIO(
- "b0gUs")
-
- scenarios = {
- 'simple': {},
- 'not-exist': {
- 'open_func_name': 'fake_open_nonexist',
- 'os_open_func_name': 'fake_os_open_nonexist',
- },
- 'not-exist-write-denied': {
- 'open_func_name': 'fake_open_nonexist',
- 'os_open_func_name': 'fake_os_open_nonexist',
- },
- 'not-exist-write-busy': {
- 'open_func_name': 'fake_open_nonexist',
- 'os_open_func_name': 'fake_os_open_nonexist',
- },
- 'exist-read-denied': {
- 'open_func_name': 'fake_open_read_denied',
- 'os_open_func_name': 'fake_os_open_read_denied',
- },
- 'exist-locked-read-denied': {
- 'locking_pid': fake_other_pid,
- 'open_func_name': 'fake_open_read_denied',
- 'os_open_func_name': 'fake_os_open_read_denied',
- },
- 'exist-empty': {},
- 'exist-invalid': {
- 'pidfile': fake_pidfile_bogus,
- },
- 'exist-current-pid': {
- 'pidfile': fake_pidfile_current_pid,
- 'pidfile_pid': fake_current_pid,
- },
- 'exist-current-pid-locked': {
- 'pidfile': fake_pidfile_current_pid,
- 'pidfile_pid': fake_current_pid,
- 'locking_pid': fake_current_pid,
- },
- 'exist-other-pid': {
- 'pidfile': fake_pidfile_other_pid,
- 'pidfile_pid': fake_other_pid,
- },
- 'exist-other-pid-locked': {
- 'pidfile': fake_pidfile_other_pid,
- 'pidfile_pid': fake_other_pid,
- 'locking_pid': fake_other_pid,
- },
- }
-
- for scenario in scenarios.values():
- scenario['pid'] = fake_current_pid
- scenario['pidfile_path'] = fake_pidfile_path
- if 'pidfile' not in scenario:
- scenario['pidfile'] = fake_pidfile_empty
- if 'pidfile_pid' not in scenario:
- scenario['pidfile_pid'] = None
- if 'locking_pid' not in scenario:
- scenario['locking_pid'] = None
- if 'open_func_name' not in scenario:
- scenario['open_func_name'] = 'fake_open_okay'
- if 'os_open_func_name' not in scenario:
- scenario['os_open_func_name'] = 'fake_os_open_okay'
-
- return scenarios
-
-
-def setup_pidfile_fixtures(testcase):
- """ Set up common fixtures for PID file test cases.
-
- :param testcase: A `TestCase` instance to decorate.
-
- Decorate the `testcase` with attributes to be fixtures for tests
- involving `PIDLockFile` instances.
-
- """
- scenarios = make_pidlockfile_scenarios()
- testcase.pidlockfile_scenarios = scenarios
-
- def get_scenario_option(testcase, key, default=None):
- value = default
- try:
- value = testcase.scenario[key]
- except (NameError, TypeError, AttributeError, KeyError):
- pass
- return value
-
- func_patcher_os_getpid = mock.patch.object(
- os, "getpid",
- return_value=scenarios['simple']['pid'])
- func_patcher_os_getpid.start()
- testcase.addCleanup(func_patcher_os_getpid.stop)
-
- def make_fake_open_funcs(testcase):
-
- def fake_open_nonexist(filename, mode, buffering):
- if mode.startswith('r'):
- error = FileNotFoundError(
- "No such file {filename!r}".format(
- filename=filename))
- raise error
- else:
- result = testcase.scenario['pidfile']
- return result
-
- def fake_open_read_denied(filename, mode, buffering):
- if mode.startswith('r'):
- error = PermissionError(
- "Read denied on {filename!r}".format(
- filename=filename))
- raise error
- else:
- result = testcase.scenario['pidfile']
- return result
-
- def fake_open_okay(filename, mode, buffering):
- result = testcase.scenario['pidfile']
- return result
-
- def fake_os_open_nonexist(filename, flags, mode):
- if (flags & os.O_CREAT):
- result = testcase.scenario['pidfile'].fileno()
- else:
- error = FileNotFoundError(
- "No such file {filename!r}".format(
- filename=filename))
- raise error
- return result
-
- def fake_os_open_read_denied(filename, flags, mode):
- if (flags & os.O_CREAT):
- result = testcase.scenario['pidfile'].fileno()
- else:
- error = PermissionError(
- "Read denied on {filename!r}".format(
- filename=filename))
- raise error
- return result
-
- def fake_os_open_okay(filename, flags, mode):
- result = testcase.scenario['pidfile'].fileno()
- return result
-
- funcs = dict(
- (name, obj) for (name, obj) in vars().items()
- if callable(obj))
-
- return funcs
-
- testcase.fake_pidfile_open_funcs = make_fake_open_funcs(testcase)
-
- def fake_open(filename, mode='rt', buffering=None):
- scenario_path = get_scenario_option(testcase, 'pidfile_path')
- if filename == scenario_path:
- func_name = testcase.scenario['open_func_name']
- fake_open_func = testcase.fake_pidfile_open_funcs[func_name]
- result = fake_open_func(filename, mode, buffering)
- else:
- result = FakeFileDescriptorStringIO()
- return result
-
- mock_open = mock.mock_open()
- mock_open.side_effect = fake_open
-
- func_patcher_builtin_open = mock.patch.object(
- builtins, "open",
- new=mock_open)
- func_patcher_builtin_open.start()
- testcase.addCleanup(func_patcher_builtin_open.stop)
-
- def fake_os_open(filename, flags, mode=None):
- scenario_path = get_scenario_option(testcase, 'pidfile_path')
- if filename == scenario_path:
- func_name = testcase.scenario['os_open_func_name']
- fake_os_open_func = testcase.fake_pidfile_open_funcs[func_name]
- result = fake_os_open_func(filename, flags, mode)
- else:
- result = FakeFileDescriptorStringIO().fileno()
- return result
-
- mock_os_open = mock.MagicMock(side_effect=fake_os_open)
-
- func_patcher_os_open = mock.patch.object(
- os, "open",
- new=mock_os_open)
- func_patcher_os_open.start()
- testcase.addCleanup(func_patcher_os_open.stop)
-
- def fake_os_fdopen(fd, mode='rt', buffering=None):
- scenario_pidfile = get_scenario_option(
- testcase, 'pidfile', FakeFileDescriptorStringIO())
- if fd == testcase.scenario['pidfile'].fileno():
- result = testcase.scenario['pidfile']
- else:
- raise OSError(errno.EBADF, "Bad file descriptor")
- return result
-
- mock_os_fdopen = mock.MagicMock(side_effect=fake_os_fdopen)
-
- func_patcher_os_fdopen = mock.patch.object(
- os, "fdopen",
- new=mock_os_fdopen)
- func_patcher_os_fdopen.start()
- testcase.addCleanup(func_patcher_os_fdopen.stop)
-
-
-def make_lockfile_method_fakes(scenario):
- """ Make common fake methods for lockfile class.
-
- :param scenario: A scenario for testing with PIDLockFile.
- :return: A mapping from normal function name to the corresponding
- fake function.
-
- Each fake function behaves appropriately for the specified `scenario`.
-
- """
-
- def fake_func_read_pid():
- return scenario['pidfile_pid']
- def fake_func_is_locked():
- return (scenario['locking_pid'] is not None)
- def fake_func_i_am_locking():
- return (
- scenario['locking_pid'] == scenario['pid'])
- def fake_func_acquire(timeout=None):
- if scenario['locking_pid'] is not None:
- raise lockfile.AlreadyLocked()
- scenario['locking_pid'] = scenario['pid']
- def fake_func_release():
- if scenario['locking_pid'] is None:
- raise lockfile.NotLocked()
- if scenario['locking_pid'] != scenario['pid']:
- raise lockfile.NotMyLock()
- scenario['locking_pid'] = None
- def fake_func_break_lock():
- scenario['locking_pid'] = None
-
- fake_methods = dict(
- (
- func_name.replace('fake_func_', ''),
- mock.MagicMock(side_effect=fake_func))
- for (func_name, fake_func) in vars().items()
- if func_name.startswith('fake_func_'))
-
- return fake_methods
-
-
-def apply_lockfile_method_mocks(mock_lockfile, testcase, scenario):
- """ Apply common fake methods to mock lockfile class.
-
- :param mock_lockfile: An object providing the `LockFile` interface.
- :param testcase: The `TestCase` instance providing the context for
- the patch.
- :param scenario: The `PIDLockFile` test scenario to use.
-
- Mock the `LockFile` methods of `mock_lockfile`, by applying fake
- methods customised for `scenario`. The mock is does by a patch
- within the context of `testcase`.
-
- """
- fake_methods = dict(
- (func_name, fake_func)
- for (func_name, fake_func) in
- make_lockfile_method_fakes(scenario).items()
- if func_name not in ['read_pid'])
-
- for (func_name, fake_func) in fake_methods.items():
- func_patcher = mock.patch.object(
- mock_lockfile, func_name,
- new=fake_func)
- func_patcher.start()
- testcase.addCleanup(func_patcher.stop)
-
-
-def setup_pidlockfile_fixtures(testcase, scenario_name=None):
- """ Set up common fixtures for PIDLockFile test cases.
-
- :param testcase: The `TestCase` instance to decorate.
- :param scenario_name: The name of the `PIDLockFile` scenario to use.
-
- Decorate the `testcase` with attributes that are fixtures for test
- cases involving `PIDLockFile` instances.`
-
- """
-
- setup_pidfile_fixtures(testcase)
-
- for func_name in [
- 'write_pid_to_pidfile',
- 'remove_existing_pidfile',
- ]:
- func_patcher = mock.patch.object(lockfile.pidlockfile, func_name)
- func_patcher.start()
- testcase.addCleanup(func_patcher.stop)
-
-
-class TimeoutPIDLockFile_TestCase(scaffold.TestCase):
- """ Test cases for ‘TimeoutPIDLockFile’ class. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(TimeoutPIDLockFile_TestCase, self).setUp()
-
- pidlockfile_scenarios = make_pidlockfile_scenarios()
- self.pidlockfile_scenario = pidlockfile_scenarios['simple']
- pidfile_path = self.pidlockfile_scenario['pidfile_path']
-
- for func_name in ['__init__', 'acquire']:
- func_patcher = mock.patch.object(
- lockfile.pidlockfile.PIDLockFile, func_name)
- func_patcher.start()
- self.addCleanup(func_patcher.stop)
-
- self.scenario = {
- 'pidfile_path': self.pidlockfile_scenario['pidfile_path'],
- 'acquire_timeout': self.getUniqueInteger(),
- }
-
- self.test_kwargs = dict(
- path=self.scenario['pidfile_path'],
- acquire_timeout=self.scenario['acquire_timeout'],
- )
- self.test_instance = daemon.pidfile.TimeoutPIDLockFile(
- **self.test_kwargs)
-
- def test_inherits_from_pidlockfile(self):
- """ Should inherit from PIDLockFile. """
- instance = self.test_instance
- self.assertIsInstance(instance, lockfile.pidlockfile.PIDLockFile)
-
- def test_init_has_expected_signature(self):
- """ Should have expected signature for ‘__init__’. """
- def test_func(self, path, acquire_timeout=None, *args, **kwargs): pass
- test_func.__name__ = str('__init__')
- self.assertFunctionSignatureMatch(
- test_func,
- daemon.pidfile.TimeoutPIDLockFile.__init__)
-
- def test_has_specified_acquire_timeout(self):
- """ Should have specified ‘acquire_timeout’ value. """
- instance = self.test_instance
- expected_timeout = self.test_kwargs['acquire_timeout']
- self.assertEqual(expected_timeout, instance.acquire_timeout)
-
- @mock.patch.object(
- lockfile.pidlockfile.PIDLockFile, "__init__",
- autospec=True)
- def test_calls_superclass_init(self, mock_init):
- """ Should call the superclass ‘__init__’. """
- expected_path = self.test_kwargs['path']
- instance = daemon.pidfile.TimeoutPIDLockFile(**self.test_kwargs)
- mock_init.assert_called_with(instance, expected_path)
-
- @mock.patch.object(
- lockfile.pidlockfile.PIDLockFile, "acquire",
- autospec=True)
- def test_acquire_uses_specified_timeout(self, mock_func_acquire):
- """ Should call the superclass ‘acquire’ with specified timeout. """
- instance = self.test_instance
- test_timeout = self.getUniqueInteger()
- expected_timeout = test_timeout
- instance.acquire(test_timeout)
- mock_func_acquire.assert_called_with(instance, expected_timeout)
-
- @mock.patch.object(
- lockfile.pidlockfile.PIDLockFile, "acquire",
- autospec=True)
- def test_acquire_uses_stored_timeout_by_default(self, mock_func_acquire):
- """ Should call superclass ‘acquire’ with stored timeout by default. """
- instance = self.test_instance
- test_timeout = self.test_kwargs['acquire_timeout']
- expected_timeout = test_timeout
- instance.acquire()
- mock_func_acquire.assert_called_with(instance, expected_timeout)
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/external_libs/python/python-daemon-2.0.5/test/test_runner.py b/external_libs/python/python-daemon-2.0.5/test/test_runner.py
deleted file mode 100644
index 4c0c714a..00000000
--- a/external_libs/python/python-daemon-2.0.5/test/test_runner.py
+++ /dev/null
@@ -1,675 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# test/test_runner.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2009–2015 Ben Finney <ben+python@benfinney.id.au>
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the Apache License, version 2.0 as published by the
-# Apache Software Foundation.
-# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
-
-""" Unit test for ‘runner’ module.
- """
-
-from __future__ import (absolute_import, unicode_literals)
-
-try:
- # Python 3 standard library.
- import builtins
-except ImportError:
- # Python 2 standard library.
- import __builtin__ as builtins
-import os
-import os.path
-import sys
-import tempfile
-import errno
-import signal
-import functools
-
-import lockfile
-import mock
-import testtools
-
-from . import scaffold
-from .scaffold import (basestring, unicode)
-from .test_pidfile import (
- FakeFileDescriptorStringIO,
- setup_pidfile_fixtures,
- make_pidlockfile_scenarios,
- apply_lockfile_method_mocks,
- )
-from .test_daemon import (
- setup_streams_fixtures,
- )
-
-import daemon.daemon
-import daemon.runner
-import daemon.pidfile
-
-
-class ModuleExceptions_TestCase(scaffold.Exception_TestCase):
- """ Test cases for module exception classes. """
-
- scenarios = scaffold.make_exception_scenarios([
- ('daemon.runner.DaemonRunnerError', dict(
- exc_type = daemon.runner.DaemonRunnerError,
- min_args = 1,
- types = [Exception],
- )),
- ('daemon.runner.DaemonRunnerInvalidActionError', dict(
- exc_type = daemon.runner.DaemonRunnerInvalidActionError,
- min_args = 1,
- types = [daemon.runner.DaemonRunnerError, ValueError],
- )),
- ('daemon.runner.DaemonRunnerStartFailureError', dict(
- exc_type = daemon.runner.DaemonRunnerStartFailureError,
- min_args = 1,
- types = [daemon.runner.DaemonRunnerError, RuntimeError],
- )),
- ('daemon.runner.DaemonRunnerStopFailureError', dict(
- exc_type = daemon.runner.DaemonRunnerStopFailureError,
- min_args = 1,
- types = [daemon.runner.DaemonRunnerError, RuntimeError],
- )),
- ])
-
-
-def make_runner_scenarios():
- """ Make a collection of scenarios for testing `DaemonRunner` instances.
-
- :return: A collection of scenarios for tests involving
- `DaemonRunner` instances.
-
- The collection is a mapping from scenario name to a dictionary of
- scenario attributes.
-
- """
-
- pidlockfile_scenarios = make_pidlockfile_scenarios()
-
- scenarios = {
- 'simple': {
- 'pidlockfile_scenario_name': 'simple',
- },
- 'pidfile-locked': {
- 'pidlockfile_scenario_name': 'exist-other-pid-locked',
- },
- }
-
- for scenario in scenarios.values():
- if 'pidlockfile_scenario_name' in scenario:
- pidlockfile_scenario = pidlockfile_scenarios.pop(
- scenario['pidlockfile_scenario_name'])
- scenario['pid'] = pidlockfile_scenario['pid']
- scenario['pidfile_path'] = pidlockfile_scenario['pidfile_path']
- scenario['pidfile_timeout'] = 23
- scenario['pidlockfile_scenario'] = pidlockfile_scenario
-
- return scenarios
-
-
-def set_runner_scenario(testcase, scenario_name):
- """ Set the DaemonRunner test scenario for the test case.
-
- :param testcase: The `TestCase` instance to decorate.
- :param scenario_name: The name of the scenario to use.
-
- Set the `DaemonRunner` test scenario name and decorate the
- `testcase` with the corresponding scenario fixtures.
-
- """
- scenarios = testcase.runner_scenarios
- testcase.scenario = scenarios[scenario_name]
- apply_lockfile_method_mocks(
- testcase.mock_runner_lockfile,
- testcase,
- testcase.scenario['pidlockfile_scenario'])
-
-
-def setup_runner_fixtures(testcase):
- """ Set up common fixtures for `DaemonRunner` test cases.
-
- :param testcase: A `TestCase` instance to decorate.
-
- Decorate the `testcase` with attributes to be fixtures for tests
- involving `DaemonRunner` instances.
-
- """
- setup_pidfile_fixtures(testcase)
- setup_streams_fixtures(testcase)
-
- testcase.runner_scenarios = make_runner_scenarios()
-
- patcher_stderr = mock.patch.object(
- sys, "stderr",
- new=FakeFileDescriptorStringIO())
- testcase.fake_stderr = patcher_stderr.start()
- testcase.addCleanup(patcher_stderr.stop)
-
- simple_scenario = testcase.runner_scenarios['simple']
-
- testcase.mock_runner_lockfile = mock.MagicMock(
- spec=daemon.pidfile.TimeoutPIDLockFile)
- apply_lockfile_method_mocks(
- testcase.mock_runner_lockfile,
- testcase,
- simple_scenario['pidlockfile_scenario'])
- testcase.mock_runner_lockfile.path = simple_scenario['pidfile_path']
-
- patcher_lockfile_class = mock.patch.object(
- daemon.pidfile, "TimeoutPIDLockFile",
- return_value=testcase.mock_runner_lockfile)
- patcher_lockfile_class.start()
- testcase.addCleanup(patcher_lockfile_class.stop)
-
- class TestApp(object):
-
- def __init__(self):
- self.stdin_path = testcase.stream_file_paths['stdin']
- self.stdout_path = testcase.stream_file_paths['stdout']
- self.stderr_path = testcase.stream_file_paths['stderr']
- self.pidfile_path = simple_scenario['pidfile_path']
- self.pidfile_timeout = simple_scenario['pidfile_timeout']
-
- run = mock.MagicMock(name="TestApp.run")
-
- testcase.TestApp = TestApp
-
- patcher_runner_daemoncontext = mock.patch.object(
- daemon.runner, "DaemonContext", autospec=True)
- patcher_runner_daemoncontext.start()
- testcase.addCleanup(patcher_runner_daemoncontext.stop)
-
- testcase.test_app = testcase.TestApp()
-
- testcase.test_program_name = "bazprog"
- testcase.test_program_path = os.path.join(
- "/foo/bar", testcase.test_program_name)
- testcase.valid_argv_params = {
- 'start': [testcase.test_program_path, 'start'],
- 'stop': [testcase.test_program_path, 'stop'],
- 'restart': [testcase.test_program_path, 'restart'],
- }
-
- def fake_open(filename, mode=None, buffering=None):
- if filename in testcase.stream_files_by_path:
- result = testcase.stream_files_by_path[filename]
- else:
- result = FakeFileDescriptorStringIO()
- result.mode = mode
- result.buffering = buffering
- return result
-
- mock_open = mock.mock_open()
- mock_open.side_effect = fake_open
-
- func_patcher_builtin_open = mock.patch.object(
- builtins, "open",
- new=mock_open)
- func_patcher_builtin_open.start()
- testcase.addCleanup(func_patcher_builtin_open.stop)
-
- func_patcher_os_kill = mock.patch.object(os, "kill")
- func_patcher_os_kill.start()
- testcase.addCleanup(func_patcher_os_kill.stop)
-
- patcher_sys_argv = mock.patch.object(
- sys, "argv",
- new=testcase.valid_argv_params['start'])
- patcher_sys_argv.start()
- testcase.addCleanup(patcher_sys_argv.stop)
-
- testcase.test_instance = daemon.runner.DaemonRunner(testcase.test_app)
-
- testcase.scenario = NotImplemented
-
-
-class DaemonRunner_BaseTestCase(scaffold.TestCase):
- """ Base class for DaemonRunner test case classes. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(DaemonRunner_BaseTestCase, self).setUp()
-
- setup_runner_fixtures(self)
- set_runner_scenario(self, 'simple')
-
-
-class DaemonRunner_TestCase(DaemonRunner_BaseTestCase):
- """ Test cases for DaemonRunner class. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(DaemonRunner_TestCase, self).setUp()
-
- func_patcher_parse_args = mock.patch.object(
- daemon.runner.DaemonRunner, "parse_args")
- func_patcher_parse_args.start()
- self.addCleanup(func_patcher_parse_args.stop)
-
- # Create a new instance now with our custom patches.
- self.test_instance = daemon.runner.DaemonRunner(self.test_app)
-
- def test_instantiate(self):
- """ New instance of DaemonRunner should be created. """
- self.assertIsInstance(self.test_instance, daemon.runner.DaemonRunner)
-
- def test_parses_commandline_args(self):
- """ Should parse commandline arguments. """
- self.test_instance.parse_args.assert_called_with()
-
- def test_has_specified_app(self):
- """ Should have specified application object. """
- self.assertIs(self.test_app, self.test_instance.app)
-
- def test_sets_pidfile_none_when_pidfile_path_is_none(self):
- """ Should set ‘pidfile’ to ‘None’ when ‘pidfile_path’ is ‘None’. """
- pidfile_path = None
- self.test_app.pidfile_path = pidfile_path
- expected_pidfile = None
- instance = daemon.runner.DaemonRunner(self.test_app)
- self.assertIs(expected_pidfile, instance.pidfile)
-
- def test_error_when_pidfile_path_not_string(self):
- """ Should raise ValueError when PID file path not a string. """
- pidfile_path = object()
- self.test_app.pidfile_path = pidfile_path
- expected_error = ValueError
- self.assertRaises(
- expected_error,
- daemon.runner.DaemonRunner, self.test_app)
-
- def test_error_when_pidfile_path_not_absolute(self):
- """ Should raise ValueError when PID file path not absolute. """
- pidfile_path = "foo/bar.pid"
- self.test_app.pidfile_path = pidfile_path
- expected_error = ValueError
- self.assertRaises(
- expected_error,
- daemon.runner.DaemonRunner, self.test_app)
-
- def test_creates_lock_with_specified_parameters(self):
- """ Should create a TimeoutPIDLockFile with specified params. """
- pidfile_path = self.scenario['pidfile_path']
- pidfile_timeout = self.scenario['pidfile_timeout']
- daemon.pidfile.TimeoutPIDLockFile.assert_called_with(
- pidfile_path, pidfile_timeout)
-
- def test_has_created_pidfile(self):
- """ Should have new PID lock file as `pidfile` attribute. """
- expected_pidfile = self.mock_runner_lockfile
- instance = self.test_instance
- self.assertIs(
- expected_pidfile, instance.pidfile)
-
- def test_daemon_context_has_created_pidfile(self):
- """ DaemonContext component should have new PID lock file. """
- expected_pidfile = self.mock_runner_lockfile
- daemon_context = self.test_instance.daemon_context
- self.assertIs(
- expected_pidfile, daemon_context.pidfile)
-
- def test_daemon_context_has_specified_stdin_stream(self):
- """ DaemonContext component should have specified stdin file. """
- test_app = self.test_app
- expected_file = self.stream_files_by_name['stdin']
- daemon_context = self.test_instance.daemon_context
- self.assertEqual(expected_file, daemon_context.stdin)
-
- def test_daemon_context_has_stdin_in_read_mode(self):
- """ DaemonContext component should open stdin file for read. """
- expected_mode = 'rt'
- daemon_context = self.test_instance.daemon_context
- self.assertIn(expected_mode, daemon_context.stdin.mode)
-
- def test_daemon_context_has_specified_stdout_stream(self):
- """ DaemonContext component should have specified stdout file. """
- test_app = self.test_app
- expected_file = self.stream_files_by_name['stdout']
- daemon_context = self.test_instance.daemon_context
- self.assertEqual(expected_file, daemon_context.stdout)
-
- def test_daemon_context_has_stdout_in_append_mode(self):
- """ DaemonContext component should open stdout file for append. """
- expected_mode = 'w+t'
- daemon_context = self.test_instance.daemon_context
- self.assertIn(expected_mode, daemon_context.stdout.mode)
-
- def test_daemon_context_has_specified_stderr_stream(self):
- """ DaemonContext component should have specified stderr file. """
- test_app = self.test_app
- expected_file = self.stream_files_by_name['stderr']
- daemon_context = self.test_instance.daemon_context
- self.assertEqual(expected_file, daemon_context.stderr)
-
- def test_daemon_context_has_stderr_in_append_mode(self):
- """ DaemonContext component should open stderr file for append. """
- expected_mode = 'w+t'
- daemon_context = self.test_instance.daemon_context
- self.assertIn(expected_mode, daemon_context.stderr.mode)
-
- def test_daemon_context_has_stderr_with_no_buffering(self):
- """ DaemonContext component should open stderr file unbuffered. """
- expected_buffering = 0
- daemon_context = self.test_instance.daemon_context
- self.assertEqual(
- expected_buffering, daemon_context.stderr.buffering)
-
-
-class DaemonRunner_usage_exit_TestCase(DaemonRunner_BaseTestCase):
- """ Test cases for DaemonRunner.usage_exit method. """
-
- def test_raises_system_exit(self):
- """ Should raise SystemExit exception. """
- instance = self.test_instance
- argv = [self.test_program_path]
- self.assertRaises(
- SystemExit,
- instance._usage_exit, argv)
-
- def test_message_follows_conventional_format(self):
- """ Should emit a conventional usage message. """
- instance = self.test_instance
- argv = [self.test_program_path]
- expected_stderr_output = """\
- usage: {progname} ...
- """.format(
- progname=self.test_program_name)
- self.assertRaises(
- SystemExit,
- instance._usage_exit, argv)
- self.assertOutputCheckerMatch(
- expected_stderr_output, self.fake_stderr.getvalue())
-
-
-class DaemonRunner_parse_args_TestCase(DaemonRunner_BaseTestCase):
- """ Test cases for DaemonRunner.parse_args method. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(DaemonRunner_parse_args_TestCase, self).setUp()
-
- func_patcher_usage_exit = mock.patch.object(
- daemon.runner.DaemonRunner, "_usage_exit",
- side_effect=NotImplementedError)
- func_patcher_usage_exit.start()
- self.addCleanup(func_patcher_usage_exit.stop)
-
- def test_emits_usage_message_if_insufficient_args(self):
- """ Should emit a usage message and exit if too few arguments. """
- instance = self.test_instance
- argv = [self.test_program_path]
- exc = self.assertRaises(
- NotImplementedError,
- instance.parse_args, argv)
- daemon.runner.DaemonRunner._usage_exit.assert_called_with(argv)
-
- def test_emits_usage_message_if_unknown_action_arg(self):
- """ Should emit a usage message and exit if unknown action. """
- instance = self.test_instance
- progname = self.test_program_name
- argv = [self.test_program_path, 'bogus']
- exc = self.assertRaises(
- NotImplementedError,
- instance.parse_args, argv)
- daemon.runner.DaemonRunner._usage_exit.assert_called_with(argv)
-
- def test_should_parse_system_argv_by_default(self):
- """ Should parse sys.argv by default. """
- instance = self.test_instance
- expected_action = 'start'
- argv = self.valid_argv_params['start']
- with mock.patch.object(sys, "argv", new=argv):
- instance.parse_args()
- self.assertEqual(expected_action, instance.action)
-
- def test_sets_action_from_first_argument(self):
- """ Should set action from first commandline argument. """
- instance = self.test_instance
- for name, argv in self.valid_argv_params.items():
- expected_action = name
- instance.parse_args(argv)
- self.assertEqual(expected_action, instance.action)
-
-
-try:
- ProcessLookupError
-except NameError:
- # Python 2 uses OSError.
- ProcessLookupError = functools.partial(OSError, errno.ESRCH)
-
-class DaemonRunner_do_action_TestCase(DaemonRunner_BaseTestCase):
- """ Test cases for DaemonRunner.do_action method. """
-
- def test_raises_error_if_unknown_action(self):
- """ Should emit a usage message and exit if action is unknown. """
- instance = self.test_instance
- instance.action = 'bogus'
- expected_error = daemon.runner.DaemonRunnerInvalidActionError
- self.assertRaises(
- expected_error,
- instance.do_action)
-
-
-class DaemonRunner_do_action_start_TestCase(DaemonRunner_BaseTestCase):
- """ Test cases for DaemonRunner.do_action method, action 'start'. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(DaemonRunner_do_action_start_TestCase, self).setUp()
-
- self.test_instance.action = 'start'
-
- def test_raises_error_if_pidfile_locked(self):
- """ Should raise error if PID file is locked. """
-
- instance = self.test_instance
- instance.daemon_context.open.side_effect = lockfile.AlreadyLocked
- pidfile_path = self.scenario['pidfile_path']
- expected_error = daemon.runner.DaemonRunnerStartFailureError
- expected_message_content = pidfile_path
- exc = self.assertRaises(
- expected_error,
- instance.do_action)
- self.assertIn(expected_message_content, unicode(exc))
-
- def test_breaks_lock_if_no_such_process(self):
- """ Should request breaking lock if PID file process is not running. """
- set_runner_scenario(self, 'pidfile-locked')
- instance = self.test_instance
- self.mock_runner_lockfile.read_pid.return_value = (
- self.scenario['pidlockfile_scenario']['pidfile_pid'])
- pidfile_path = self.scenario['pidfile_path']
- test_pid = self.scenario['pidlockfile_scenario']['pidfile_pid']
- expected_signal = signal.SIG_DFL
- test_error = ProcessLookupError("Not running")
- os.kill.side_effect = test_error
- instance.do_action()
- os.kill.assert_called_with(test_pid, expected_signal)
- self.mock_runner_lockfile.break_lock.assert_called_with()
-
- def test_requests_daemon_context_open(self):
- """ Should request the daemon context to open. """
- instance = self.test_instance
- instance.do_action()
- instance.daemon_context.open.assert_called_with()
-
- def test_emits_start_message_to_stderr(self):
- """ Should emit start message to stderr. """
- instance = self.test_instance
- expected_stderr = """\
- started with pid {pid:d}
- """.format(
- pid=self.scenario['pid'])
- instance.do_action()
- self.assertOutputCheckerMatch(
- expected_stderr, self.fake_stderr.getvalue())
-
- def test_requests_app_run(self):
- """ Should request the application to run. """
- instance = self.test_instance
- instance.do_action()
- self.test_app.run.assert_called_with()
-
-
-class DaemonRunner_do_action_stop_TestCase(DaemonRunner_BaseTestCase):
- """ Test cases for DaemonRunner.do_action method, action 'stop'. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(DaemonRunner_do_action_stop_TestCase, self).setUp()
-
- set_runner_scenario(self, 'pidfile-locked')
-
- self.test_instance.action = 'stop'
-
- self.mock_runner_lockfile.is_locked.return_value = True
- self.mock_runner_lockfile.i_am_locking.return_value = False
- self.mock_runner_lockfile.read_pid.return_value = (
- self.scenario['pidlockfile_scenario']['pidfile_pid'])
-
- def test_raises_error_if_pidfile_not_locked(self):
- """ Should raise error if PID file is not locked. """
- set_runner_scenario(self, 'simple')
- instance = self.test_instance
- self.mock_runner_lockfile.is_locked.return_value = False
- self.mock_runner_lockfile.i_am_locking.return_value = False
- self.mock_runner_lockfile.read_pid.return_value = (
- self.scenario['pidlockfile_scenario']['pidfile_pid'])
- pidfile_path = self.scenario['pidfile_path']
- expected_error = daemon.runner.DaemonRunnerStopFailureError
- expected_message_content = pidfile_path
- exc = self.assertRaises(
- expected_error,
- instance.do_action)
- self.assertIn(expected_message_content, unicode(exc))
-
- def test_breaks_lock_if_pidfile_stale(self):
- """ Should break lock if PID file is stale. """
- instance = self.test_instance
- pidfile_path = self.scenario['pidfile_path']
- test_pid = self.scenario['pidlockfile_scenario']['pidfile_pid']
- expected_signal = signal.SIG_DFL
- test_error = OSError(errno.ESRCH, "Not running")
- os.kill.side_effect = test_error
- instance.do_action()
- self.mock_runner_lockfile.break_lock.assert_called_with()
-
- def test_sends_terminate_signal_to_process_from_pidfile(self):
- """ Should send SIGTERM to the daemon process. """
- instance = self.test_instance
- test_pid = self.scenario['pidlockfile_scenario']['pidfile_pid']
- expected_signal = signal.SIGTERM
- instance.do_action()
- os.kill.assert_called_with(test_pid, expected_signal)
-
- def test_raises_error_if_cannot_send_signal_to_process(self):
- """ Should raise error if cannot send signal to daemon process. """
- instance = self.test_instance
- test_pid = self.scenario['pidlockfile_scenario']['pidfile_pid']
- pidfile_path = self.scenario['pidfile_path']
- test_error = OSError(errno.EPERM, "Nice try")
- os.kill.side_effect = test_error
- expected_error = daemon.runner.DaemonRunnerStopFailureError
- expected_message_content = unicode(test_pid)
- exc = self.assertRaises(
- expected_error,
- instance.do_action)
- self.assertIn(expected_message_content, unicode(exc))
-
-
-@mock.patch.object(daemon.runner.DaemonRunner, "_start")
-@mock.patch.object(daemon.runner.DaemonRunner, "_stop")
-class DaemonRunner_do_action_restart_TestCase(DaemonRunner_BaseTestCase):
- """ Test cases for DaemonRunner.do_action method, action 'restart'. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(DaemonRunner_do_action_restart_TestCase, self).setUp()
-
- set_runner_scenario(self, 'pidfile-locked')
-
- self.test_instance.action = 'restart'
-
- def test_requests_stop_then_start(
- self,
- mock_func_daemonrunner_start, mock_func_daemonrunner_stop):
- """ Should request stop, then start. """
- instance = self.test_instance
- instance.do_action()
- mock_func_daemonrunner_start.assert_called_with()
- mock_func_daemonrunner_stop.assert_called_with()
-
-
-@mock.patch.object(sys, "stderr")
-class emit_message_TestCase(scaffold.TestCase):
- """ Test cases for ‘emit_message’ function. """
-
- def test_writes_specified_message_to_stream(self, mock_stderr):
- """ Should write specified message to stream. """
- test_message = self.getUniqueString()
- expected_content = "{message}\n".format(message=test_message)
- daemon.runner.emit_message(test_message, stream=mock_stderr)
- mock_stderr.write.assert_called_with(expected_content)
-
- def test_writes_to_specified_stream(self, mock_stderr):
- """ Should write message to specified stream. """
- test_message = self.getUniqueString()
- mock_stream = mock.MagicMock()
- daemon.runner.emit_message(test_message, stream=mock_stream)
- mock_stream.write.assert_called_with(mock.ANY)
-
- def test_writes_to_stderr_by_default(self, mock_stderr):
- """ Should write message to ‘sys.stderr’ by default. """
- test_message = self.getUniqueString()
- daemon.runner.emit_message(test_message)
- mock_stderr.write.assert_called_with(mock.ANY)
-
-
-class is_pidfile_stale_TestCase(scaffold.TestCase):
- """ Test cases for ‘is_pidfile_stale’ function. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(is_pidfile_stale_TestCase, self).setUp()
-
- func_patcher_os_kill = mock.patch.object(os, "kill")
- func_patcher_os_kill.start()
- self.addCleanup(func_patcher_os_kill.stop)
- os.kill.return_value = None
-
- self.test_pid = self.getUniqueInteger()
- self.test_pidfile = mock.MagicMock(daemon.pidfile.TimeoutPIDLockFile)
- self.test_pidfile.read_pid.return_value = self.test_pid
-
- def test_returns_false_if_no_pid_in_file(self):
- """ Should return False if the pidfile contains no PID. """
- self.test_pidfile.read_pid.return_value = None
- expected_result = False
- result = daemon.runner.is_pidfile_stale(self.test_pidfile)
- self.assertEqual(expected_result, result)
-
- def test_returns_false_if_process_exists(self):
- """ Should return False if the process with its PID exists. """
- expected_result = False
- result = daemon.runner.is_pidfile_stale(self.test_pidfile)
- self.assertEqual(expected_result, result)
-
- def test_returns_true_if_process_does_not_exist(self):
- """ Should return True if the process does not exist. """
- test_error = ProcessLookupError("No such process")
- del os.kill.return_value
- os.kill.side_effect = test_error
- expected_result = True
- result = daemon.runner.is_pidfile_stale(self.test_pidfile)
- self.assertEqual(expected_result, result)
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/external_libs/python/python-daemon-2.0.5/test_version.py b/external_libs/python/python-daemon-2.0.5/test_version.py
deleted file mode 100644
index b52f521d..00000000
--- a/external_libs/python/python-daemon-2.0.5/test_version.py
+++ /dev/null
@@ -1,1373 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# test_version.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation; version 3 of that license or any later version.
-# No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details.
-
-""" Unit test for ‘version’ packaging module. """
-
-from __future__ import (absolute_import, unicode_literals)
-
-import os
-import os.path
-import io
-import errno
-import functools
-import collections
-import textwrap
-import json
-import tempfile
-import distutils.dist
-import distutils.cmd
-import distutils.errors
-import distutils.fancy_getopt
-try:
- # Standard library of Python 2.7 and later.
- from io import StringIO
-except ImportError:
- # Standard library of Python 2.6 and earlier.
- from StringIO import StringIO
-
-import mock
-import testtools
-import testscenarios
-import docutils
-import docutils.writers
-import docutils.nodes
-import setuptools
-import setuptools.command
-
-import version
-
-version.ensure_class_bases_begin_with(
- version.__dict__, str('VersionInfoWriter'), docutils.writers.Writer)
-version.ensure_class_bases_begin_with(
- version.__dict__, str('VersionInfoTranslator'),
- docutils.nodes.SparseNodeVisitor)
-
-
-def make_test_classes_for_ensure_class_bases_begin_with():
- """ Make test classes for use with ‘ensure_class_bases_begin_with’.
-
- :return: Mapping {`name`: `type`} of the custom types created.
-
- """
-
- class quux_metaclass(type):
- def __new__(metaclass, name, bases, namespace):
- return super(quux_metaclass, metaclass).__new__(
- metaclass, name, bases, namespace)
-
- class Foo(object):
- __metaclass__ = type
-
- class Bar(object):
- pass
-
- class FooInheritingBar(Bar):
- __metaclass__ = type
-
- class FooWithCustomMetaclass(object):
- __metaclass__ = quux_metaclass
-
- result = dict(
- (name, value) for (name, value) in locals().items()
- if isinstance(value, type))
-
- return result
-
-class ensure_class_bases_begin_with_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘ensure_class_bases_begin_with’ function. """
-
- test_classes = make_test_classes_for_ensure_class_bases_begin_with()
-
- scenarios = [
- ('simple', {
- 'test_class': test_classes['Foo'],
- 'base_class': test_classes['Bar'],
- }),
- ('custom metaclass', {
- 'test_class': test_classes['FooWithCustomMetaclass'],
- 'base_class': test_classes['Bar'],
- 'expected_metaclass': test_classes['quux_metaclass'],
- }),
- ]
-
- def setUp(self):
- """ Set up test fixtures. """
- super(ensure_class_bases_begin_with_TestCase, self).setUp()
-
- self.class_name = self.test_class.__name__
- self.test_module_namespace = {self.class_name: self.test_class}
-
- if not hasattr(self, 'expected_metaclass'):
- self.expected_metaclass = type
-
- patcher_metaclass = mock.patch.object(
- self.test_class, '__metaclass__')
- patcher_metaclass.start()
- self.addCleanup(patcher_metaclass.stop)
-
- self.fake_new_class = type(object)
- self.test_class.__metaclass__.return_value = (
- self.fake_new_class)
-
- def test_module_namespace_contains_new_class(self):
- """ Specified module namespace should have new class. """
- version.ensure_class_bases_begin_with(
- self.test_module_namespace, self.class_name, self.base_class)
- self.assertIn(self.fake_new_class, self.test_module_namespace.values())
-
- def test_calls_metaclass_with_expected_class_name(self):
- """ Should call the metaclass with the expected class name. """
- version.ensure_class_bases_begin_with(
- self.test_module_namespace, self.class_name, self.base_class)
- expected_class_name = self.class_name
- self.test_class.__metaclass__.assert_called_with(
- expected_class_name, mock.ANY, mock.ANY)
-
- def test_calls_metaclass_with_expected_bases(self):
- """ Should call the metaclass with the expected bases. """
- version.ensure_class_bases_begin_with(
- self.test_module_namespace, self.class_name, self.base_class)
- expected_bases = tuple(
- [self.base_class]
- + list(self.test_class.__bases__))
- self.test_class.__metaclass__.assert_called_with(
- mock.ANY, expected_bases, mock.ANY)
-
- def test_calls_metaclass_with_expected_namespace(self):
- """ Should call the metaclass with the expected class namespace. """
- version.ensure_class_bases_begin_with(
- self.test_module_namespace, self.class_name, self.base_class)
- expected_namespace = self.test_class.__dict__.copy()
- del expected_namespace['__dict__']
- self.test_class.__metaclass__.assert_called_with(
- mock.ANY, mock.ANY, expected_namespace)
-
-
-class ensure_class_bases_begin_with_AlreadyHasBase_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘ensure_class_bases_begin_with’ function.
-
- These test cases test the conditions where the class's base is
- already the specified base class.
-
- """
-
- test_classes = make_test_classes_for_ensure_class_bases_begin_with()
-
- scenarios = [
- ('already Bar subclass', {
- 'test_class': test_classes['FooInheritingBar'],
- 'base_class': test_classes['Bar'],
- }),
- ]
-
- def setUp(self):
- """ Set up test fixtures. """
- super(
- ensure_class_bases_begin_with_AlreadyHasBase_TestCase,
- self).setUp()
-
- self.class_name = self.test_class.__name__
- self.test_module_namespace = {self.class_name: self.test_class}
-
- patcher_metaclass = mock.patch.object(
- self.test_class, '__metaclass__')
- patcher_metaclass.start()
- self.addCleanup(patcher_metaclass.stop)
-
- def test_metaclass_not_called(self):
- """ Should not call metaclass to create a new type. """
- version.ensure_class_bases_begin_with(
- self.test_module_namespace, self.class_name, self.base_class)
- self.assertFalse(self.test_class.__metaclass__.called)
-
-
-class VersionInfoWriter_TestCase(testtools.TestCase):
- """ Test cases for ‘VersionInfoWriter’ class. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(VersionInfoWriter_TestCase, self).setUp()
-
- self.test_instance = version.VersionInfoWriter()
-
- def test_declares_version_info_support(self):
- """ Should declare support for ‘version_info’. """
- instance = self.test_instance
- expected_support = "version_info"
- result = instance.supports(expected_support)
- self.assertTrue(result)
-
-
-class VersionInfoWriter_translate_TestCase(testtools.TestCase):
- """ Test cases for ‘VersionInfoWriter.translate’ method. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(VersionInfoWriter_translate_TestCase, self).setUp()
-
- patcher_translator = mock.patch.object(
- version, 'VersionInfoTranslator')
- self.mock_class_translator = patcher_translator.start()
- self.addCleanup(patcher_translator.stop)
- self.mock_translator = self.mock_class_translator.return_value
-
- self.test_instance = version.VersionInfoWriter()
- patcher_document = mock.patch.object(
- self.test_instance, 'document')
- patcher_document.start()
- self.addCleanup(patcher_document.stop)
-
- def test_creates_translator_with_document(self):
- """ Should create a translator with the writer's document. """
- instance = self.test_instance
- expected_document = self.test_instance.document
- instance.translate()
- self.mock_class_translator.assert_called_with(expected_document)
-
- def test_calls_document_walkabout_with_translator(self):
- """ Should call document.walkabout with the translator. """
- instance = self.test_instance
- instance.translate()
- instance.document.walkabout.assert_called_with(self.mock_translator)
-
- def test_output_from_translator_astext(self):
- """ Should have output from translator.astext(). """
- instance = self.test_instance
- instance.translate()
- expected_output = self.mock_translator.astext.return_value
- self.assertEqual(expected_output, instance.output)
-
-
-class ChangeLogEntry_TestCase(testtools.TestCase):
- """ Test cases for ‘ChangeLogEntry’ class. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(ChangeLogEntry_TestCase, self).setUp()
-
- self.test_instance = version.ChangeLogEntry()
-
- def test_instantiate(self):
- """ New instance of ‘ChangeLogEntry’ should be created. """
- self.assertIsInstance(
- self.test_instance, version.ChangeLogEntry)
-
- def test_minimum_zero_arguments(self):
- """ Initialiser should not require any arguments. """
- instance = version.ChangeLogEntry()
- self.assertIsNot(instance, None)
-
-
-class ChangeLogEntry_release_date_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘ChangeLogEntry.release_date’ attribute. """
-
- scenarios = [
- ('default', {
- 'test_args': {},
- 'expected_release_date':
- version.ChangeLogEntry.default_release_date,
- }),
- ('unknown token', {
- 'test_args': {'release_date': "UNKNOWN"},
- 'expected_release_date': "UNKNOWN",
- }),
- ('future token', {
- 'test_args': {'release_date': "FUTURE"},
- 'expected_release_date': "FUTURE",
- }),
- ('2001-01-01', {
- 'test_args': {'release_date': "2001-01-01"},
- 'expected_release_date': "2001-01-01",
- }),
- ('bogus', {
- 'test_args': {'release_date': "b0gUs"},
- 'expected_error': ValueError,
- }),
- ]
-
- def test_has_expected_release_date(self):
- """ Should have default `release_date` attribute. """
- if hasattr(self, 'expected_error'):
- self.assertRaises(
- self.expected_error,
- version.ChangeLogEntry, **self.test_args)
- else:
- instance = version.ChangeLogEntry(**self.test_args)
- self.assertEqual(self.expected_release_date, instance.release_date)
-
-
-class ChangeLogEntry_version_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘ChangeLogEntry.version’ attribute. """
-
- scenarios = [
- ('default', {
- 'test_args': {},
- 'expected_version':
- version.ChangeLogEntry.default_version,
- }),
- ('unknown token', {
- 'test_args': {'version': "UNKNOWN"},
- 'expected_version': "UNKNOWN",
- }),
- ('0.0', {
- 'test_args': {'version': "0.0"},
- 'expected_version': "0.0",
- }),
- ]
-
- def test_has_expected_version(self):
- """ Should have default `version` attribute. """
- instance = version.ChangeLogEntry(**self.test_args)
- self.assertEqual(self.expected_version, instance.version)
-
-
-class ChangeLogEntry_maintainer_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘ChangeLogEntry.maintainer’ attribute. """
-
- scenarios = [
- ('default', {
- 'test_args': {},
- 'expected_maintainer': None,
- }),
- ('person', {
- 'test_args': {'maintainer': "Foo Bar <foo.bar@example.org>"},
- 'expected_maintainer': "Foo Bar <foo.bar@example.org>",
- }),
- ('bogus', {
- 'test_args': {'maintainer': "b0gUs"},
- 'expected_error': ValueError,
- }),
- ]
-
- def test_has_expected_maintainer(self):
- """ Should have default `maintainer` attribute. """
- if hasattr(self, 'expected_error'):
- self.assertRaises(
- self.expected_error,
- version.ChangeLogEntry, **self.test_args)
- else:
- instance = version.ChangeLogEntry(**self.test_args)
- self.assertEqual(self.expected_maintainer, instance.maintainer)
-
-
-class ChangeLogEntry_body_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘ChangeLogEntry.body’ attribute. """
-
- scenarios = [
- ('default', {
- 'test_args': {},
- 'expected_body': None,
- }),
- ('simple', {
- 'test_args': {'body': "Foo bar baz."},
- 'expected_body': "Foo bar baz.",
- }),
- ]
-
- def test_has_expected_body(self):
- """ Should have default `body` attribute. """
- instance = version.ChangeLogEntry(**self.test_args)
- self.assertEqual(self.expected_body, instance.body)
-
-
-class ChangeLogEntry_as_version_info_entry_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘ChangeLogEntry.as_version_info_entry’ attribute. """
-
- scenarios = [
- ('default', {
- 'test_args': {},
- 'expected_result': collections.OrderedDict([
- ('release_date', version.ChangeLogEntry.default_release_date),
- ('version', version.ChangeLogEntry.default_version),
- ('maintainer', None),
- ('body', None),
- ]),
- }),
- ]
-
- def setUp(self):
- """ Set up test fixtures. """
- super(ChangeLogEntry_as_version_info_entry_TestCase, self).setUp()
-
- self.test_instance = version.ChangeLogEntry(**self.test_args)
-
- def test_returns_result(self):
- """ Should return expected result. """
- result = self.test_instance.as_version_info_entry()
- self.assertEqual(self.expected_result, result)
-
-
-def make_mock_field_node(field_name, field_body):
- """ Make a mock Docutils field node for tests. """
-
- mock_field_node = mock.MagicMock(
- name='field', spec=docutils.nodes.field)
-
- mock_field_name_node = mock.MagicMock(
- name='field_name', spec=docutils.nodes.field_name)
- mock_field_name_node.parent = mock_field_node
- mock_field_name_node.children = [field_name]
-
- mock_field_body_node = mock.MagicMock(
- name='field_body', spec=docutils.nodes.field_body)
- mock_field_body_node.parent = mock_field_node
- mock_field_body_node.children = [field_body]
-
- mock_field_node.children = [mock_field_name_node, mock_field_body_node]
-
- def fake_func_first_child_matching_class(node_class):
- result = None
- node_class_name = node_class.__name__
- for (index, node) in enumerate(mock_field_node.children):
- if node._mock_name == node_class_name:
- result = index
- break
- return result
-
- mock_field_node.first_child_matching_class.side_effect = (
- fake_func_first_child_matching_class)
-
- return mock_field_node
-
-
-class JsonEqual(testtools.matchers.Matcher):
- """ A matcher to compare the value of JSON streams. """
-
- def __init__(self, expected):
- self.expected_value = expected
-
- def match(self, content):
- """ Assert the JSON `content` matches the `expected_content`. """
- result = None
- actual_value = json.loads(content.decode('utf-8'))
- if actual_value != self.expected_value:
- result = JsonValueMismatch(self.expected_value, actual_value)
- return result
-
-
-class JsonValueMismatch(testtools.matchers.Mismatch):
- """ The specified JSON stream does not evaluate to the expected value. """
-
- def __init__(self, expected, actual):
- self.expected_value = expected
- self.actual_value = actual
-
- def describe(self):
- """ Emit a text description of this mismatch. """
- expected_json_text = json.dumps(self.expected_value, indent=4)
- actual_json_text = json.dumps(self.actual_value, indent=4)
- text = (
- "\n"
- "reference: {expected}\n"
- "actual: {actual}\n").format(
- expected=expected_json_text, actual=actual_json_text)
- return text
-
-
-class changelog_to_version_info_collection_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘changelog_to_version_info_collection’ function. """
-
- scenarios = [
- ('single entry', {
- 'test_input': textwrap.dedent("""\
- Version 1.0
- ===========
-
- :Released: 2009-01-01
- :Maintainer: Foo Bar <foo.bar@example.org>
-
- * Lorem ipsum dolor sit amet.
- """),
- 'expected_version_info': [
- {
- 'release_date': "2009-01-01",
- 'version': "1.0",
- 'maintainer': "Foo Bar <foo.bar@example.org>",
- 'body': "* Lorem ipsum dolor sit amet.\n",
- },
- ],
- }),
- ('multiple entries', {
- 'test_input': textwrap.dedent("""\
- Version 1.0
- ===========
-
- :Released: 2009-01-01
- :Maintainer: Foo Bar <foo.bar@example.org>
-
- * Lorem ipsum dolor sit amet.
-
-
- Version 0.8
- ===========
-
- :Released: 2004-01-01
- :Maintainer: Foo Bar <foo.bar@example.org>
-
- * Donec venenatis nisl aliquam ipsum.
-
-
- Version 0.7.2
- =============
-
- :Released: 2001-01-01
- :Maintainer: Foo Bar <foo.bar@example.org>
-
- * Pellentesque elementum mollis finibus.
- """),
- 'expected_version_info': [
- {
- 'release_date': "2009-01-01",
- 'version': "1.0",
- 'maintainer': "Foo Bar <foo.bar@example.org>",
- 'body': "* Lorem ipsum dolor sit amet.\n",
- },
- {
- 'release_date': "2004-01-01",
- 'version': "0.8",
- 'maintainer': "Foo Bar <foo.bar@example.org>",
- 'body': "* Donec venenatis nisl aliquam ipsum.\n",
- },
- {
- 'release_date': "2001-01-01",
- 'version': "0.7.2",
- 'maintainer': "Foo Bar <foo.bar@example.org>",
- 'body': "* Pellentesque elementum mollis finibus.\n",
- },
- ],
- }),
- ('trailing comment', {
- 'test_input': textwrap.dedent("""\
- Version NEXT
- ============
-
- :Released: FUTURE
- :Maintainer:
-
- * Lorem ipsum dolor sit amet.
-
- ..
- Vivamus aliquam felis rutrum rutrum dictum.
- """),
- 'expected_version_info': [
- {
- 'release_date': "FUTURE",
- 'version': "NEXT",
- 'maintainer': "",
- 'body': "* Lorem ipsum dolor sit amet.\n",
- },
- ],
- }),
- ('inline comment', {
- 'test_input': textwrap.dedent("""\
- Version NEXT
- ============
-
- :Released: FUTURE
- :Maintainer:
-
- ..
- Vivamus aliquam felis rutrum rutrum dictum.
-
- * Lorem ipsum dolor sit amet.
- """),
- 'expected_version_info': [
- {
- 'release_date': "FUTURE",
- 'version': "NEXT",
- 'maintainer': "",
- 'body': "* Lorem ipsum dolor sit amet.\n",
- },
- ],
- }),
- ('unreleased entry', {
- 'test_input': textwrap.dedent("""\
- Version NEXT
- ============
-
- :Released: FUTURE
- :Maintainer:
-
- * Lorem ipsum dolor sit amet.
-
-
- Version 0.8
- ===========
-
- :Released: 2001-01-01
- :Maintainer: Foo Bar <foo.bar@example.org>
-
- * Donec venenatis nisl aliquam ipsum.
- """),
- 'expected_version_info': [
- {
- 'release_date': "FUTURE",
- 'version': "NEXT",
- 'maintainer': "",
- 'body': "* Lorem ipsum dolor sit amet.\n",
- },
- {
- 'release_date': "2001-01-01",
- 'version': "0.8",
- 'maintainer': "Foo Bar <foo.bar@example.org>",
- 'body': "* Donec venenatis nisl aliquam ipsum.\n",
- },
- ],
- }),
- ('no section', {
- 'test_input': textwrap.dedent("""\
- :Released: 2009-01-01
- :Maintainer: Foo Bar <foo.bar@example.org>
-
- * Lorem ipsum dolor sit amet.
- """),
- 'expected_error': version.InvalidFormatError,
- }),
- ('subsection', {
- 'test_input': textwrap.dedent("""\
- Version 1.0
- ===========
-
- :Released: 2009-01-01
- :Maintainer: Foo Bar <foo.bar@example.org>
-
- * Lorem ipsum dolor sit amet.
-
- Ut ultricies fermentum quam
- ---------------------------
-
- * In commodo magna facilisis in.
- """),
- 'expected_error': version.InvalidFormatError,
- 'subsection': True,
- }),
- ('unknown field', {
- 'test_input': textwrap.dedent("""\
- Version 1.0
- ===========
-
- :Released: 2009-01-01
- :Maintainer: Foo Bar <foo.bar@example.org>
- :Favourite: Spam
-
- * Lorem ipsum dolor sit amet.
- """),
- 'expected_error': version.InvalidFormatError,
- }),
- ('invalid version word', {
- 'test_input': textwrap.dedent("""\
- BoGuS 1.0
- =========
-
- :Released: 2009-01-01
- :Maintainer: Foo Bar <foo.bar@example.org>
-
- * Lorem ipsum dolor sit amet.
- """),
- 'expected_error': version.InvalidFormatError,
- }),
- ('invalid section title', {
- 'test_input': textwrap.dedent("""\
- Lorem Ipsum 1.0
- ===============
-
- :Released: 2009-01-01
- :Maintainer: Foo Bar <foo.bar@example.org>
-
- * Lorem ipsum dolor sit amet.
- """),
- 'expected_error': version.InvalidFormatError,
- }),
- ]
-
- def test_returns_expected_version_info(self):
- """ Should return expected version info mapping. """
- infile = StringIO(self.test_input)
- if hasattr(self, 'expected_error'):
- self.assertRaises(
- self.expected_error,
- version.changelog_to_version_info_collection, infile)
- else:
- result = version.changelog_to_version_info_collection(infile)
- self.assertThat(result, JsonEqual(self.expected_version_info))
-
-
-try:
- FileNotFoundError
- PermissionError
-except NameError:
- # Python 2 uses OSError.
- FileNotFoundError = functools.partial(IOError, errno.ENOENT)
- PermissionError = functools.partial(IOError, errno.EPERM)
-
-fake_version_info = {
- 'release_date': "2001-01-01", 'version': "2.0",
- 'maintainer': None, 'body': None,
- }
-
-@mock.patch.object(
- version, "get_latest_version", return_value=fake_version_info)
-class generate_version_info_from_changelog_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘generate_version_info_from_changelog’ function. """
-
- fake_open_side_effects = {
- 'success': (
- lambda *args, **kwargs: StringIO()),
- 'file not found': FileNotFoundError(),
- 'permission denied': PermissionError(),
- }
-
- scenarios = [
- ('simple', {
- 'open_scenario': 'success',
- 'fake_versions_json': json.dumps([fake_version_info]),
- 'expected_result': fake_version_info,
- }),
- ('file not found', {
- 'open_scenario': 'file not found',
- 'expected_result': {},
- }),
- ('permission denied', {
- 'open_scenario': 'permission denied',
- 'expected_result': {},
- }),
- ]
-
- def setUp(self):
- """ Set up test fixtures. """
- super(generate_version_info_from_changelog_TestCase, self).setUp()
-
- self.fake_changelog_file_path = tempfile.mktemp()
-
- def fake_open(filespec, *args, **kwargs):
- if filespec == self.fake_changelog_file_path:
- side_effect = self.fake_open_side_effects[self.open_scenario]
- if callable(side_effect):
- result = side_effect()
- else:
- raise side_effect
- else:
- result = StringIO()
- return result
-
- func_patcher_io_open = mock.patch.object(
- io, "open")
- func_patcher_io_open.start()
- self.addCleanup(func_patcher_io_open.stop)
- io.open.side_effect = fake_open
-
- self.file_encoding = "utf-8"
-
- func_patcher_changelog_to_version_info_collection = mock.patch.object(
- version, "changelog_to_version_info_collection")
- func_patcher_changelog_to_version_info_collection.start()
- self.addCleanup(func_patcher_changelog_to_version_info_collection.stop)
- if hasattr(self, 'fake_versions_json'):
- version.changelog_to_version_info_collection.return_value = (
- self.fake_versions_json.encode(self.file_encoding))
-
- def test_returns_empty_collection_on_read_error(
- self,
- mock_func_get_latest_version):
- """ Should return empty collection on error reading changelog. """
- test_error = PermissionError("Not for you")
- version.changelog_to_version_info_collection.side_effect = test_error
- result = version.generate_version_info_from_changelog(
- self.fake_changelog_file_path)
- expected_result = {}
- self.assertDictEqual(expected_result, result)
-
- def test_opens_file_with_expected_encoding(
- self,
- mock_func_get_latest_version):
- """ Should open changelog file in text mode with expected encoding. """
- result = version.generate_version_info_from_changelog(
- self.fake_changelog_file_path)
- expected_file_path = self.fake_changelog_file_path
- expected_open_mode = 'rt'
- expected_encoding = self.file_encoding
- (open_args_positional, open_args_kwargs) = io.open.call_args
- (open_args_filespec, open_args_mode) = open_args_positional[:2]
- open_args_encoding = open_args_kwargs['encoding']
- self.assertEqual(expected_file_path, open_args_filespec)
- self.assertEqual(expected_open_mode, open_args_mode)
- self.assertEqual(expected_encoding, open_args_encoding)
-
- def test_returns_expected_result(
- self,
- mock_func_get_latest_version):
- """ Should return expected result. """
- result = version.generate_version_info_from_changelog(
- self.fake_changelog_file_path)
- self.assertEqual(self.expected_result, result)
-
-
-DefaultNoneDict = functools.partial(collections.defaultdict, lambda: None)
-
-class get_latest_version_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘get_latest_version’ function. """
-
- scenarios = [
- ('simple', {
- 'test_versions': [
- DefaultNoneDict({'release_date': "LATEST"}),
- ],
- 'expected_result': version.ChangeLogEntry.make_ordered_dict(
- DefaultNoneDict({'release_date': "LATEST"})),
- }),
- ('no versions', {
- 'test_versions': [],
- 'expected_result': collections.OrderedDict(),
- }),
- ('ordered versions', {
- 'test_versions': [
- DefaultNoneDict({'release_date': "1"}),
- DefaultNoneDict({'release_date': "2"}),
- DefaultNoneDict({'release_date': "LATEST"}),
- ],
- 'expected_result': version.ChangeLogEntry.make_ordered_dict(
- DefaultNoneDict({'release_date': "LATEST"})),
- }),
- ('un-ordered versions', {
- 'test_versions': [
- DefaultNoneDict({'release_date': "2"}),
- DefaultNoneDict({'release_date': "LATEST"}),
- DefaultNoneDict({'release_date': "1"}),
- ],
- 'expected_result': version.ChangeLogEntry.make_ordered_dict(
- DefaultNoneDict({'release_date': "LATEST"})),
- }),
- ]
-
- def test_returns_expected_result(self):
- """ Should return expected result. """
- result = version.get_latest_version(self.test_versions)
- self.assertDictEqual(self.expected_result, result)
-
-
-@mock.patch.object(json, "dumps", side_effect=json.dumps)
-class serialise_version_info_from_mapping_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘get_latest_version’ function. """
-
- scenarios = [
- ('simple', {
- 'test_version_info': {'foo': "spam"},
- }),
- ]
-
- for (name, scenario) in scenarios:
- scenario['fake_json_dump'] = json.dumps(scenario['test_version_info'])
- scenario['expected_value'] = scenario['test_version_info']
-
- def test_passes_specified_object(self, mock_func_json_dumps):
- """ Should pass the specified object to `json.dumps`. """
- result = version.serialise_version_info_from_mapping(
- self.test_version_info)
- mock_func_json_dumps.assert_called_with(
- self.test_version_info, indent=mock.ANY)
-
- def test_returns_expected_result(self, mock_func_json_dumps):
- """ Should return expected result. """
- mock_func_json_dumps.return_value = self.fake_json_dump
- result = version.serialise_version_info_from_mapping(
- self.test_version_info)
- value = json.loads(result)
- self.assertEqual(self.expected_value, value)
-
-
-DistributionMetadata_defaults = {
- name: None
- for name in list(collections.OrderedDict.fromkeys(
- distutils.dist.DistributionMetadata._METHOD_BASENAMES))}
-FakeDistributionMetadata = collections.namedtuple(
- 'FakeDistributionMetadata', DistributionMetadata_defaults.keys())
-
-Distribution_defaults = {
- 'metadata': None,
- 'version': None,
- 'release_date': None,
- 'maintainer': None,
- 'maintainer_email': None,
- }
-FakeDistribution = collections.namedtuple(
- 'FakeDistribution', Distribution_defaults.keys())
-
-def make_fake_distribution(
- fields_override=None, metadata_fields_override=None):
- metadata_fields = DistributionMetadata_defaults.copy()
- if metadata_fields_override is not None:
- metadata_fields.update(metadata_fields_override)
- metadata = FakeDistributionMetadata(**metadata_fields)
-
- fields = Distribution_defaults.copy()
- fields['metadata'] = metadata
- if fields_override is not None:
- fields.update(fields_override)
- distribution = FakeDistribution(**fields)
-
- return distribution
-
-
-class get_changelog_path_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘get_changelog_path’ function. """
-
- default_path = "."
- default_script_filename = "setup.py"
-
- scenarios = [
- ('simple', {}),
- ('unusual script name', {
- 'script_filename': "lorem_ipsum",
- }),
- ('relative script path', {
- 'script_directory': "dolor/sit/amet",
- }),
- ('absolute script path', {
- 'script_directory': "/dolor/sit/amet",
- }),
- ('specify filename', {
- 'changelog_filename': "adipiscing",
- }),
- ]
-
- def setUp(self):
- """ Set up test fixtures. """
- super(get_changelog_path_TestCase, self).setUp()
-
- self.test_distribution = mock.MagicMock(distutils.dist.Distribution)
-
- if not hasattr(self, 'script_directory'):
- self.script_directory = self.default_path
- if not hasattr(self, 'script_filename'):
- self.script_filename = self.default_script_filename
- self.test_distribution.script_name = os.path.join(
- self.script_directory, self.script_filename)
-
- changelog_filename = version.changelog_filename
- if hasattr(self, 'changelog_filename'):
- changelog_filename = self.changelog_filename
-
- self.expected_result = os.path.join(
- self.script_directory, changelog_filename)
-
- def test_returns_expected_result(self):
- """ Should return expected result. """
- args = {
- 'distribution': self.test_distribution,
- }
- if hasattr(self, 'changelog_filename'):
- args.update({'filename': self.changelog_filename})
- result = version.get_changelog_path(**args)
- self.assertEqual(self.expected_result, result)
-
-
-class WriteVersionInfoCommand_BaseTestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Base class for ‘WriteVersionInfoCommand’ test case classes. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(WriteVersionInfoCommand_BaseTestCase, self).setUp()
-
- fake_distribution_name = self.getUniqueString()
-
- self.test_distribution = distutils.dist.Distribution()
- self.test_distribution.metadata.name = fake_distribution_name
-
-
-class WriteVersionInfoCommand_TestCase(WriteVersionInfoCommand_BaseTestCase):
- """ Test cases for ‘WriteVersionInfoCommand’ class. """
-
- def test_subclass_of_distutils_command(self):
- """ Should be a subclass of ‘distutils.cmd.Command’. """
- instance = version.WriteVersionInfoCommand(self.test_distribution)
- self.assertIsInstance(instance, distutils.cmd.Command)
-
-
-class WriteVersionInfoCommand_user_options_TestCase(
- WriteVersionInfoCommand_BaseTestCase):
- """ Test cases for ‘WriteVersionInfoCommand.user_options’ attribute. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(WriteVersionInfoCommand_user_options_TestCase, self).setUp()
-
- self.test_instance = version.WriteVersionInfoCommand(
- self.test_distribution)
- self.commandline_parser = distutils.fancy_getopt.FancyGetopt(
- self.test_instance.user_options)
-
- def test_parses_correctly_as_fancy_getopt(self):
- """ Should parse correctly in ‘FancyGetopt’. """
- self.assertIsInstance(
- self.commandline_parser, distutils.fancy_getopt.FancyGetopt)
-
- def test_includes_base_class_user_options(self):
- """ Should include base class's user_options. """
- base_command = setuptools.command.egg_info.egg_info
- expected_user_options = base_command.user_options
- self.assertThat(
- set(expected_user_options),
- IsSubset(set(self.test_instance.user_options)))
-
- def test_has_option_changelog_path(self):
- """ Should have a ‘changelog-path’ option. """
- expected_option_name = "changelog-path="
- result = self.commandline_parser.has_option(expected_option_name)
- self.assertTrue(result)
-
- def test_has_option_outfile_path(self):
- """ Should have a ‘outfile-path’ option. """
- expected_option_name = "outfile-path="
- result = self.commandline_parser.has_option(expected_option_name)
- self.assertTrue(result)
-
-
-class WriteVersionInfoCommand_initialize_options_TestCase(
- WriteVersionInfoCommand_BaseTestCase):
- """ Test cases for ‘WriteVersionInfoCommand.initialize_options’ method. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(
- WriteVersionInfoCommand_initialize_options_TestCase, self
- ).setUp()
-
- patcher_func_egg_info_initialize_options = mock.patch.object(
- setuptools.command.egg_info.egg_info, "initialize_options")
- patcher_func_egg_info_initialize_options.start()
- self.addCleanup(patcher_func_egg_info_initialize_options.stop)
-
- def test_calls_base_class_method(self):
- """ Should call base class's ‘initialize_options’ method. """
- instance = version.WriteVersionInfoCommand(self.test_distribution)
- base_command_class = setuptools.command.egg_info.egg_info
- base_command_class.initialize_options.assert_called_with()
-
- def test_sets_changelog_path_to_none(self):
- """ Should set ‘changelog_path’ attribute to ``None``. """
- instance = version.WriteVersionInfoCommand(self.test_distribution)
- self.assertIs(instance.changelog_path, None)
-
- def test_sets_outfile_path_to_none(self):
- """ Should set ‘outfile_path’ attribute to ``None``. """
- instance = version.WriteVersionInfoCommand(self.test_distribution)
- self.assertIs(instance.outfile_path, None)
-
-
-class WriteVersionInfoCommand_finalize_options_TestCase(
- WriteVersionInfoCommand_BaseTestCase):
- """ Test cases for ‘WriteVersionInfoCommand.finalize_options’ method. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(WriteVersionInfoCommand_finalize_options_TestCase, self).setUp()
-
- self.test_instance = version.WriteVersionInfoCommand(self.test_distribution)
-
- patcher_func_egg_info_finalize_options = mock.patch.object(
- setuptools.command.egg_info.egg_info, "finalize_options")
- patcher_func_egg_info_finalize_options.start()
- self.addCleanup(patcher_func_egg_info_finalize_options.stop)
-
- self.fake_script_dir = self.getUniqueString()
- self.test_distribution.script_name = os.path.join(
- self.fake_script_dir, self.getUniqueString())
-
- self.fake_egg_dir = self.getUniqueString()
- self.test_instance.egg_info = self.fake_egg_dir
-
- patcher_func_get_changelog_path = mock.patch.object(
- version, "get_changelog_path")
- patcher_func_get_changelog_path.start()
- self.addCleanup(patcher_func_get_changelog_path.stop)
-
- self.fake_changelog_path = self.getUniqueString()
- version.get_changelog_path.return_value = self.fake_changelog_path
-
- def test_calls_base_class_method(self):
- """ Should call base class's ‘finalize_options’ method. """
- base_command_class = setuptools.command.egg_info.egg_info
- self.test_instance.finalize_options()
- base_command_class.finalize_options.assert_called_with()
-
- def test_sets_force_to_none(self):
- """ Should set ‘force’ attribute to ``None``. """
- self.test_instance.finalize_options()
- self.assertIs(self.test_instance.force, None)
-
- def test_sets_changelog_path_using_get_changelog_path(self):
- """ Should set ‘changelog_path’ attribute if it was ``None``. """
- self.test_instance.changelog_path = None
- self.test_instance.finalize_options()
- expected_changelog_path = self.fake_changelog_path
- self.assertEqual(expected_changelog_path, self.test_instance.changelog_path)
-
- def test_leaves_changelog_path_if_already_set(self):
- """ Should leave ‘changelog_path’ attribute set. """
- prior_changelog_path = self.getUniqueString()
- self.test_instance.changelog_path = prior_changelog_path
- self.test_instance.finalize_options()
- expected_changelog_path = prior_changelog_path
- self.assertEqual(expected_changelog_path, self.test_instance.changelog_path)
-
- def test_sets_outfile_path_to_default(self):
- """ Should set ‘outfile_path’ attribute to default value. """
- fake_version_info_filename = self.getUniqueString()
- with mock.patch.object(
- version, "version_info_filename",
- new=fake_version_info_filename):
- self.test_instance.finalize_options()
- expected_outfile_path = os.path.join(
- self.fake_egg_dir, fake_version_info_filename)
- self.assertEqual(expected_outfile_path, self.test_instance.outfile_path)
-
- def test_leaves_outfile_path_if_already_set(self):
- """ Should leave ‘outfile_path’ attribute set. """
- prior_outfile_path = self.getUniqueString()
- self.test_instance.outfile_path = prior_outfile_path
- self.test_instance.finalize_options()
- expected_outfile_path = prior_outfile_path
- self.assertEqual(expected_outfile_path, self.test_instance.outfile_path)
-
-
-class has_changelog_TestCase(
- testscenarios.WithScenarios, testtools.TestCase):
- """ Test cases for ‘has_changelog’ function. """
-
- fake_os_path_exists_side_effects = {
- 'true': (lambda path: True),
- 'false': (lambda path: False),
- }
-
- scenarios = [
- ('no changelog path', {
- 'changelog_path': None,
- 'expected_result': False,
- }),
- ('changelog exists', {
- 'os_path_exists_scenario': 'true',
- 'expected_result': True,
- }),
- ('changelog not found', {
- 'os_path_exists_scenario': 'false',
- 'expected_result': False,
- }),
- ]
-
- def setUp(self):
- """ Set up test fixtures. """
- super(has_changelog_TestCase, self).setUp()
-
- self.test_distribution = distutils.dist.Distribution()
- self.test_command = version.EggInfoCommand(
- self.test_distribution)
-
- patcher_func_get_changelog_path = mock.patch.object(
- version, "get_changelog_path")
- patcher_func_get_changelog_path.start()
- self.addCleanup(patcher_func_get_changelog_path.stop)
-
- self.fake_changelog_file_path = self.getUniqueString()
- if hasattr(self, 'changelog_path'):
- self.fake_changelog_file_path = self.changelog_path
- version.get_changelog_path.return_value = self.fake_changelog_file_path
- self.fake_changelog_file = StringIO()
-
- def fake_os_path_exists(path):
- if path == self.fake_changelog_file_path:
- side_effect = self.fake_os_path_exists_side_effects[
- self.os_path_exists_scenario]
- if callable(side_effect):
- result = side_effect(path)
- else:
- raise side_effect
- else:
- result = False
- return result
-
- func_patcher_os_path_exists = mock.patch.object(
- os.path, "exists")
- func_patcher_os_path_exists.start()
- self.addCleanup(func_patcher_os_path_exists.stop)
- os.path.exists.side_effect = fake_os_path_exists
-
- def test_gets_changelog_path_from_distribution(self):
- """ Should call ‘get_changelog_path’ with distribution. """
- result = version.has_changelog(self.test_command)
- version.get_changelog_path.assert_called_with(
- self.test_distribution)
-
- def test_returns_expected_result(self):
- """ Should be a subclass of ‘distutils.cmd.Command’. """
- result = version.has_changelog(self.test_command)
- self.assertEqual(self.expected_result, result)
-
-
-@mock.patch.object(version, 'generate_version_info_from_changelog')
-@mock.patch.object(version, 'serialise_version_info_from_mapping')
-@mock.patch.object(version.EggInfoCommand, "write_file")
-class WriteVersionInfoCommand_run_TestCase(
- WriteVersionInfoCommand_BaseTestCase):
- """ Test cases for ‘WriteVersionInfoCommand.run’ method. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(WriteVersionInfoCommand_run_TestCase, self).setUp()
-
- self.test_instance = version.WriteVersionInfoCommand(
- self.test_distribution)
-
- self.fake_changelog_path = self.getUniqueString()
- self.test_instance.changelog_path = self.fake_changelog_path
-
- self.fake_outfile_path = self.getUniqueString()
- self.test_instance.outfile_path = self.fake_outfile_path
-
- def test_returns_none(
- self,
- mock_func_egg_info_write_file,
- mock_func_serialise_version_info,
- mock_func_generate_version_info):
- """ Should return ``None``. """
- result = self.test_instance.run()
- self.assertIs(result, None)
-
- def test_generates_version_info_from_changelog(
- self,
- mock_func_egg_info_write_file,
- mock_func_serialise_version_info,
- mock_func_generate_version_info):
- """ Should generate version info from specified changelog. """
- self.test_instance.run()
- expected_changelog_path = self.test_instance.changelog_path
- mock_func_generate_version_info.assert_called_with(
- expected_changelog_path)
-
- def test_serialises_version_info_from_mapping(
- self,
- mock_func_egg_info_write_file,
- mock_func_serialise_version_info,
- mock_func_generate_version_info):
- """ Should serialise version info from specified mapping. """
- self.test_instance.run()
- expected_version_info = mock_func_generate_version_info.return_value
- mock_func_serialise_version_info.assert_called_with(
- expected_version_info)
-
- def test_writes_file_using_command_context(
- self,
- mock_func_egg_info_write_file,
- mock_func_serialise_version_info,
- mock_func_generate_version_info):
- """ Should write the metadata file using the command context. """
- self.test_instance.run()
- expected_content = mock_func_serialise_version_info.return_value
- mock_func_egg_info_write_file.assert_called_with(
- "version info", self.fake_outfile_path, expected_content)
-
-
-IsSubset = testtools.matchers.MatchesPredicateWithParams(
- set.issubset, "{0} should be a subset of {1}")
-
-class EggInfoCommand_TestCase(testtools.TestCase):
- """ Test cases for ‘EggInfoCommand’ class. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(EggInfoCommand_TestCase, self).setUp()
-
- self.test_distribution = distutils.dist.Distribution()
- self.test_instance = version.EggInfoCommand(self.test_distribution)
-
- def test_subclass_of_setuptools_egg_info(self):
- """ Should be a subclass of Setuptools ‘egg_info’. """
- self.assertIsInstance(
- self.test_instance, setuptools.command.egg_info.egg_info)
-
- def test_sub_commands_include_base_class_sub_commands(self):
- """ Should include base class's sub-commands in this sub_commands. """
- base_command = setuptools.command.egg_info.egg_info
- expected_sub_commands = base_command.sub_commands
- self.assertThat(
- set(expected_sub_commands),
- IsSubset(set(self.test_instance.sub_commands)))
-
- def test_sub_commands_includes_write_version_info_command(self):
- """ Should include sub-command named ‘write_version_info’. """
- commands_by_name = dict(self.test_instance.sub_commands)
- expected_predicate = version.has_changelog
- expected_item = ('write_version_info', expected_predicate)
- self.assertIn(expected_item, commands_by_name.items())
-
-
-@mock.patch.object(setuptools.command.egg_info.egg_info, "run")
-class EggInfoCommand_run_TestCase(testtools.TestCase):
- """ Test cases for ‘EggInfoCommand.run’ method. """
-
- def setUp(self):
- """ Set up test fixtures. """
- super(EggInfoCommand_run_TestCase, self).setUp()
-
- self.test_distribution = distutils.dist.Distribution()
- self.test_instance = version.EggInfoCommand(self.test_distribution)
-
- base_command = setuptools.command.egg_info.egg_info
- patcher_func_egg_info_get_sub_commands = mock.patch.object(
- base_command, "get_sub_commands")
- patcher_func_egg_info_get_sub_commands.start()
- self.addCleanup(patcher_func_egg_info_get_sub_commands.stop)
-
- patcher_func_egg_info_run_command = mock.patch.object(
- base_command, "run_command")
- patcher_func_egg_info_run_command.start()
- self.addCleanup(patcher_func_egg_info_run_command.stop)
-
- self.fake_sub_commands = ["spam", "eggs", "beans"]
- base_command.get_sub_commands.return_value = self.fake_sub_commands
-
- def test_returns_none(self, mock_func_egg_info_run):
- """ Should return ``None``. """
- result = self.test_instance.run()
- self.assertIs(result, None)
-
- def test_runs_each_command_in_sub_commands(
- self, mock_func_egg_info_run):
- """ Should run each command in ‘self.get_sub_commands()’. """
- base_command = setuptools.command.egg_info.egg_info
- self.test_instance.run()
- expected_calls = [mock.call(name) for name in self.fake_sub_commands]
- base_command.run_command.assert_has_calls(expected_calls)
-
- def test_calls_base_class_run(self, mock_func_egg_info_run):
- """ Should call base class's ‘run’ method. """
- result = self.test_instance.run()
- mock_func_egg_info_run.assert_called_with()
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :
diff --git a/external_libs/python/python-daemon-2.0.5/version.py b/external_libs/python/python-daemon-2.0.5/version.py
deleted file mode 100644
index 7e4c4202..00000000
--- a/external_libs/python/python-daemon-2.0.5/version.py
+++ /dev/null
@@ -1,547 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# version.py
-# Part of ‘python-daemon’, an implementation of PEP 3143.
-#
-# Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
-#
-# This is free software: you may copy, modify, and/or distribute this work
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation; version 3 of that license or any later version.
-# No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details.
-
-""" Version information unified for human- and machine-readable formats.
-
- The project ‘ChangeLog’ file is a reStructuredText document, with
- each section describing a version of the project. The document is
- intended to be readable as-is by end users.
-
- This module handles transformation from the ‘ChangeLog’ to a
- mapping of version information, serialised as JSON. It also
- provides functionality for Distutils to use this information.
-
- Requires:
-
- * Docutils <http://docutils.sourceforge.net/>
- * JSON <https://docs.python.org/3/reference/json.html>
-
- """
-
-from __future__ import (absolute_import, unicode_literals)
-
-import sys
-import os
-import io
-import errno
-import json
-import datetime
-import textwrap
-import re
-import functools
-import collections
-import distutils
-import distutils.errors
-import distutils.cmd
-try:
- # Python 2 has both ‘str’ (bytes) and ‘unicode’ (text).
- basestring = basestring
- unicode = unicode
-except NameError:
- # Python 3 names the Unicode data type ‘str’.
- basestring = str
- unicode = str
-
-import setuptools
-import setuptools.command.egg_info
-
-
-def ensure_class_bases_begin_with(namespace, class_name, base_class):
- """ Ensure the named class's bases start with the base class.
-
- :param namespace: The namespace containing the class name.
- :param class_name: The name of the class to alter.
- :param base_class: The type to be the first base class for the
- newly created type.
- :return: ``None``.
-
- This function is a hack to circumvent a circular dependency:
- using classes from a module which is not installed at the time
- this module is imported.
-
- Call this function after ensuring `base_class` is available,
- before using the class named by `class_name`.
-
- """
- existing_class = namespace[class_name]
- assert isinstance(existing_class, type)
-
- bases = list(existing_class.__bases__)
- if base_class is bases[0]:
- # Already bound to a type with the right bases.
- return
- bases.insert(0, base_class)
-
- new_class_namespace = existing_class.__dict__.copy()
- # Type creation will assign the correct ‘__dict__’ attribute.
- del new_class_namespace['__dict__']
-
- metaclass = existing_class.__metaclass__
- new_class = metaclass(class_name, tuple(bases), new_class_namespace)
-
- namespace[class_name] = new_class
-
-
-class VersionInfoWriter(object):
- """ Docutils writer to produce a version info JSON data stream. """
-
- # This class needs its base class to be a class from `docutils`.
- # But that would create a circular dependency: Setuptools cannot
- # ensure `docutils` is available before importing this module.
- #
- # Use `ensure_class_bases_begin_with` after importing `docutils`, to
- # re-bind the `VersionInfoWriter` name to a new type that inherits
- # from `docutils.writers.Writer`.
-
- __metaclass__ = type
-
- supported = ['version_info']
- """ Formats this writer supports. """
-
- def __init__(self):
- super(VersionInfoWriter, self).__init__()
- self.translator_class = VersionInfoTranslator
-
- def translate(self):
- visitor = self.translator_class(self.document)
- self.document.walkabout(visitor)
- self.output = visitor.astext()
-
-
-rfc822_person_regex = re.compile(
- "^(?P<name>[^<]+) <(?P<email>[^>]+)>$")
-
-class ChangeLogEntry:
- """ An individual entry from the ‘ChangeLog’ document. """
-
- __metaclass__ = type
-
- field_names = [
- 'release_date',
- 'version',
- 'maintainer',
- 'body',
- ]
-
- date_format = "%Y-%m-%d"
- default_version = "UNKNOWN"
- default_release_date = "UNKNOWN"
-
- def __init__(
- self,
- release_date=default_release_date, version=default_version,
- maintainer=None, body=None):
- self.validate_release_date(release_date)
- self.release_date = release_date
-
- self.version = version
-
- self.validate_maintainer(maintainer)
- self.maintainer = maintainer
- self.body = body
-
- @classmethod
- def validate_release_date(cls, value):
- """ Validate the `release_date` value.
-
- :param value: The prospective `release_date` value.
- :return: ``None`` if the value is valid.
- :raises ValueError: If the value is invalid.
-
- """
- if value in ["UNKNOWN", "FUTURE"]:
- # A valid non-date value.
- return None
-
- # Raises `ValueError` if parse fails.
- datetime.datetime.strptime(value, ChangeLogEntry.date_format)
-
- @classmethod
- def validate_maintainer(cls, value):
- """ Validate the `maintainer` value.
-
- :param value: The prospective `maintainer` value.
- :return: ``None`` if the value is valid.
- :raises ValueError: If the value is invalid.
-
- """
- valid = False
-
- if value is None:
- valid = True
- elif rfc822_person_regex.search(value):
- valid = True
-
- if not valid:
- raise ValueError("Not a valid person specification {value!r}")
- else:
- return None
-
- @classmethod
- def make_ordered_dict(cls, fields):
- """ Make an ordered dict of the fields. """
- result = collections.OrderedDict(
- (name, fields[name])
- for name in cls.field_names)
- return result
-
- def as_version_info_entry(self):
- """ Format the changelog entry as a version info entry. """
- fields = vars(self)
- entry = self.make_ordered_dict(fields)
-
- return entry
-
-
-class InvalidFormatError(ValueError):
- """ Raised when the document is not a valid ‘ChangeLog’ document. """
-
-
-class VersionInfoTranslator(object):
- """ Translator from document nodes to a version info stream. """
-
- # This class needs its base class to be a class from `docutils`.
- # But that would create a circular dependency: Setuptools cannot
- # ensure `docutils` is available before importing this module.
- #
- # Use `ensure_class_bases_begin_with` after importing `docutils`,
- # to re-bind the `VersionInfoTranslator` name to a new type that
- # inherits from `docutils.nodes.SparseNodeVisitor`.
-
- __metaclass__ = type
-
- wrap_width = 78
- bullet_text = "* "
-
- attr_convert_funcs_by_attr_name = {
- 'released': ('release_date', unicode),
- 'version': ('version', unicode),
- 'maintainer': ('maintainer', unicode),
- }
-
- def __init__(self, document):
- super(VersionInfoTranslator, self).__init__(document)
- self.settings = document.settings
- self.current_section_level = 0
- self.current_field_name = None
- self.content = []
- self.indent_width = 0
- self.initial_indent = ""
- self.subsequent_indent = ""
- self.current_entry = None
-
- # Docutils is not available when this class is defined.
- # Get the `docutils` module dynamically.
- self._docutils = sys.modules['docutils']
-
- def astext(self):
- """ Return the translated document as text. """
- text = json.dumps(self.content, indent=4)
- return text
-
- def append_to_current_entry(self, text):
- if self.current_entry is not None:
- if self.current_entry.body is not None:
- self.current_entry.body += text
-
- def visit_Text(self, node):
- raw_text = node.astext()
- text = textwrap.fill(
- raw_text,
- width=self.wrap_width,
- initial_indent=self.initial_indent,
- subsequent_indent=self.subsequent_indent)
- self.append_to_current_entry(text)
-
- def depart_Text(self, node):
- pass
-
- def visit_comment(self, node):
- raise self._docutils.nodes.SkipNode
-
- def visit_field_body(self, node):
- field_list_node = node.parent.parent
- if not isinstance(field_list_node, self._docutils.nodes.field_list):
- raise InvalidFormatError(
- "Unexpected field within {node!r}".format(
- node=field_list_node))
- (attr_name, convert_func) = self.attr_convert_funcs_by_attr_name[
- self.current_field_name]
- attr_value = convert_func(node.astext())
- setattr(self.current_entry, attr_name, attr_value)
-
- def depart_field_body(self, node):
- pass
-
- def visit_field_list(self, node):
- pass
-
- def depart_field_list(self, node):
- self.current_field_name = None
- self.current_entry.body = ""
-
- def visit_field_name(self, node):
- field_name = node.astext()
- if self.current_section_level == 1:
- # At a top-level section.
- if field_name.lower() not in ["released", "maintainer"]:
- raise InvalidFormatError(
- "Unexpected field name {name!r}".format(name=field_name))
- self.current_field_name = field_name.lower()
-
- def depart_field_name(self, node):
- pass
-
- def visit_bullet_list(self, node):
- self.current_context = []
-
- def depart_bullet_list(self, node):
- self.current_entry.changes = self.current_context
- self.current_context = None
-
- def adjust_indent_width(self, delta):
- self.indent_width += delta
- self.subsequent_indent = " " * self.indent_width
- self.initial_indent = self.subsequent_indent
-
- def visit_list_item(self, node):
- indent_delta = +len(self.bullet_text)
- self.adjust_indent_width(indent_delta)
- self.initial_indent = self.subsequent_indent[:-indent_delta]
- self.append_to_current_entry(self.initial_indent + self.bullet_text)
-
- def depart_list_item(self, node):
- indent_delta = +len(self.bullet_text)
- self.adjust_indent_width(-indent_delta)
- self.append_to_current_entry("\n")
-
- def visit_section(self, node):
- self.current_section_level += 1
- if self.current_section_level == 1:
- # At a top-level section.
- self.current_entry = ChangeLogEntry()
- else:
- raise InvalidFormatError(
- "Subsections not implemented for this writer")
-
- def depart_section(self, node):
- self.current_section_level -= 1
- self.content.append(
- self.current_entry.as_version_info_entry())
- self.current_entry = None
-
- _expected_title_word_length = len("Version FOO".split(" "))
-
- def depart_title(self, node):
- title_text = node.astext()
- # At a top-level section.
- words = title_text.split(" ")
- version = None
- if len(words) != self._expected_title_word_length:
- raise InvalidFormatError(
- "Unexpected title text {text!r}".format(text=title_text))
- if words[0].lower() not in ["version"]:
- raise InvalidFormatError(
- "Unexpected title text {text!r}".format(text=title_text))
- version = words[-1]
- self.current_entry.version = version
-
-
-def changelog_to_version_info_collection(infile):
- """ Render the ‘ChangeLog’ document to a version info collection.
-
- :param infile: A file-like object containing the changelog.
- :return: The serialised JSON data of the version info collection.
-
- """
-
- # Docutils is not available when Setuptools needs this module, so
- # delay the imports to this function instead.
- import docutils.core
- import docutils.nodes
- import docutils.writers
-
- ensure_class_bases_begin_with(
- globals(), str('VersionInfoWriter'), docutils.writers.Writer)
- ensure_class_bases_begin_with(
- globals(), str('VersionInfoTranslator'),
- docutils.nodes.SparseNodeVisitor)
-
- writer = VersionInfoWriter()
- settings_overrides = {
- 'doctitle_xform': False,
- }
- version_info_json = docutils.core.publish_string(
- infile.read(), writer=writer,
- settings_overrides=settings_overrides)
-
- return version_info_json
-
-
-try:
- lru_cache = functools.lru_cache
-except AttributeError:
- # Python < 3.2 does not have the `functools.lru_cache` function.
- # Not essential, so replace it with a no-op.
- lru_cache = lambda maxsize=None, typed=False: lambda func: func
-
-
-@lru_cache(maxsize=128)
-def generate_version_info_from_changelog(infile_path):
- """ Get the version info for the latest version in the changelog.
-
- :param infile_path: Filesystem path to the input changelog file.
- :return: The generated version info mapping; or ``None`` if the
- file cannot be read.
-
- The document is explicitly opened as UTF-8 encoded text.
-
- """
- version_info = collections.OrderedDict()
-
- versions_all_json = None
- try:
- with io.open(infile_path, 'rt', encoding="utf-8") as infile:
- versions_all_json = changelog_to_version_info_collection(infile)
- except EnvironmentError:
- # If we can't read the input file, leave the collection empty.
- pass
-
- if versions_all_json is not None:
- versions_all = json.loads(versions_all_json.decode('utf-8'))
- version_info = get_latest_version(versions_all)
-
- return version_info
-
-
-def get_latest_version(versions):
- """ Get the latest version from a collection of changelog entries.
-
- :param versions: A collection of mappings for changelog entries.
- :return: An ordered mapping of fields for the latest version,
- if `versions` is non-empty; otherwise, an empty mapping.
-
- """
- version_info = collections.OrderedDict()
-
- versions_by_release_date = {
- item['release_date']: item
- for item in versions}
- if versions_by_release_date:
- latest_release_date = max(versions_by_release_date.keys())
- version_info = ChangeLogEntry.make_ordered_dict(
- versions_by_release_date[latest_release_date])
-
- return version_info
-
-
-def serialise_version_info_from_mapping(version_info):
- """ Generate the version info serialised data.
-
- :param version_info: Mapping of version info items.
- :return: The version info serialised to JSON.
-
- """
- content = json.dumps(version_info, indent=4)
-
- return content
-
-
-changelog_filename = "ChangeLog"
-
-def get_changelog_path(distribution, filename=changelog_filename):
- """ Get the changelog file path for the distribution.
-
- :param distribution: The distutils.dist.Distribution instance.
- :param filename: The base filename of the changelog document.
- :return: Filesystem path of the changelog document, or ``None``
- if not discoverable.
-
- """
- setup_dirname = os.path.dirname(distribution.script_name)
- filepath = os.path.join(setup_dirname, filename)
-
- return filepath
-
-
-def has_changelog(command):
- """ Return ``True`` iff the distribution's changelog file exists. """
- result = False
-
- changelog_path = get_changelog_path(command.distribution)
- if changelog_path is not None:
- if os.path.exists(changelog_path):
- result = True
-
- return result
-
-
-class EggInfoCommand(setuptools.command.egg_info.egg_info, object):
- """ Custom ‘egg_info’ command for this distribution. """
-
- sub_commands = ([
- ('write_version_info', has_changelog),
- ] + setuptools.command.egg_info.egg_info.sub_commands)
-
- def run(self):
- """ Execute this command. """
- super(EggInfoCommand, self).run()
-
- for command_name in self.get_sub_commands():
- self.run_command(command_name)
-
-
-version_info_filename = "version_info.json"
-
-class WriteVersionInfoCommand(EggInfoCommand, object):
- """ Setuptools command to serialise version info metadata. """
-
- user_options = ([
- ("changelog-path=", None,
- "Filesystem path to the changelog document."),
- ("outfile-path=", None,
- "Filesystem path to the version info file."),
- ] + EggInfoCommand.user_options)
-
- def initialize_options(self):
- """ Initialise command options to defaults. """
- super(WriteVersionInfoCommand, self).initialize_options()
- self.changelog_path = None
- self.outfile_path = None
-
- def finalize_options(self):
- """ Finalise command options before execution. """
- self.set_undefined_options(
- 'build',
- ('force', 'force'))
-
- super(WriteVersionInfoCommand, self).finalize_options()
-
- if self.changelog_path is None:
- self.changelog_path = get_changelog_path(self.distribution)
-
- if self.outfile_path is None:
- egg_dir = self.egg_info
- self.outfile_path = os.path.join(egg_dir, version_info_filename)
-
- def run(self):
- """ Execute this command. """
- version_info = generate_version_info_from_changelog(self.changelog_path)
- content = serialise_version_info_from_mapping(version_info)
- self.write_file("version info", self.outfile_path, content)
-
-
-# Local variables:
-# coding: utf-8
-# mode: python
-# End:
-# vim: fileencoding=utf-8 filetype=python :