import rcssmin
This commit is contained in:
parent
c5cf1641ea
commit
0a77228dc5
39
.gitignore
vendored
39
.gitignore
vendored
@ -1,36 +1,13 @@
|
|||||||
|
Makefile
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
*.so
|
||||||
|
|
||||||
# Packages
|
|
||||||
*.egg
|
|
||||||
*.egg-info
|
|
||||||
dist
|
dist
|
||||||
build
|
build
|
||||||
eggs
|
|
||||||
parts
|
|
||||||
bin
|
|
||||||
var
|
|
||||||
sdist
|
|
||||||
develop-eggs
|
|
||||||
.installed.cfg
|
|
||||||
lib
|
|
||||||
lib64
|
|
||||||
__pycache__
|
__pycache__
|
||||||
|
docs/_userdoc/_build/
|
||||||
# Installer logs
|
docs/apidoc/
|
||||||
pip-log.txt
|
docs/userdoc/
|
||||||
|
_website/
|
||||||
# Unit test / coverage reports
|
*.ebuild
|
||||||
.coverage
|
.*.sw?
|
||||||
.tox
|
MANIFEST
|
||||||
nosetests.xml
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
|
|
||||||
# Mr Developer
|
|
||||||
.mr.developer.cfg
|
|
||||||
.project
|
|
||||||
.pydevproject
|
|
||||||
|
201
LICENSE
Normal file
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
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.
|
152
README.rst
Normal file
152
README.rst
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
.. -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
=====================================
|
||||||
|
rCSSmin - A CSS Minifier For Python
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
TABLE OF CONTENTS
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
1. Introduction
|
||||||
|
2. Copyright and License
|
||||||
|
3. System Requirements
|
||||||
|
4. Installation
|
||||||
|
5. Documentation
|
||||||
|
6. Bugs
|
||||||
|
7. Author Information
|
||||||
|
|
||||||
|
|
||||||
|
INTRODUCTION
|
||||||
|
------------
|
||||||
|
|
||||||
|
RCSSmin is a CSS minifier written in python.
|
||||||
|
|
||||||
|
The minifier is based on the semantics of the `YUI compressor`_\, which itself
|
||||||
|
is based on `the rule list by Isaac Schlueter`_\.
|
||||||
|
|
||||||
|
This module is a re-implementation aiming for speed instead of maximum
|
||||||
|
compression, so it can be used at runtime (rather than during a preprocessing
|
||||||
|
step). RCSSmin does syntactical compression only (removing spaces, comments
|
||||||
|
and possibly semicolons). It does not provide semantic compression (like
|
||||||
|
removing empty blocks, collapsing redundant properties etc). It does, however,
|
||||||
|
support various CSS hacks (by keeping them working as intended).
|
||||||
|
|
||||||
|
Here's a feature list:
|
||||||
|
|
||||||
|
- Strings are kept, except that escaped newlines are stripped
|
||||||
|
- Space/Comments before the very end or before various characters are
|
||||||
|
stripped: ``:{});=>+],!`` (The colon (``:``) is a special case, a single
|
||||||
|
space is kept if it's outside a ruleset.)
|
||||||
|
- Space/Comments at the very beginning or after various characters are
|
||||||
|
stripped: ``{}(=:>+[,!``
|
||||||
|
- Optional space after unicode escapes is kept, resp. replaced by a simple
|
||||||
|
space
|
||||||
|
- whitespaces inside ``url()`` definitions are stripped
|
||||||
|
- Comments starting with an exclamation mark (``!``) can be kept optionally.
|
||||||
|
- All other comments and/or whitespace characters are replaced by a single
|
||||||
|
space.
|
||||||
|
- Multiple consecutive semicolons are reduced to one
|
||||||
|
- The last semicolon within a ruleset is stripped
|
||||||
|
- CSS Hacks supported:
|
||||||
|
|
||||||
|
- IE7 hack (``>/**/``)
|
||||||
|
- Mac-IE5 hack (``/*\*/.../**/``)
|
||||||
|
- The boxmodelhack is supported naturally because it relies on valid CSS2
|
||||||
|
strings
|
||||||
|
- Between ``:first-line`` and the following comma or curly brace a space is
|
||||||
|
inserted. (apparently it's needed for IE6)
|
||||||
|
- Same for ``:first-letter``
|
||||||
|
|
||||||
|
rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to
|
||||||
|
factor 50 or so (depending on the input).
|
||||||
|
|
||||||
|
.. _YUI compressor: https://github.com/yui/yuicompressor/
|
||||||
|
|
||||||
|
.. _the rule list by Isaac Schlueter: https://github.com/isaacs/cssmin/tree/
|
||||||
|
|
||||||
|
|
||||||
|
COPYRIGHT AND LICENSE
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Copyright 2011 - 2014
|
||||||
|
André Malo or his licensors, as applicable.
|
||||||
|
|
||||||
|
The whole package (except for the files in the bench/ directory)
|
||||||
|
is distributed under the Apache License Version 2.0. You'll find a copy in the
|
||||||
|
root directory of the distribution or online at:
|
||||||
|
<http://www.apache.org/licenses/LICENSE-2.0>.
|
||||||
|
|
||||||
|
|
||||||
|
SYSTEM REQUIREMENTS
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Both python 2 (>=2.4) and python 3 are supported.
|
||||||
|
|
||||||
|
|
||||||
|
INSTALLATION
|
||||||
|
------------
|
||||||
|
|
||||||
|
Using pip
|
||||||
|
~~~~~~~~~
|
||||||
|
|
||||||
|
$ pip install rcssmin
|
||||||
|
|
||||||
|
|
||||||
|
Using distutils
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
$ python setup.py install
|
||||||
|
|
||||||
|
The following extra options to the install command may be of interest:
|
||||||
|
|
||||||
|
--without-c-extensions Don't install C extensions
|
||||||
|
--without-docs Do not install documentation files
|
||||||
|
|
||||||
|
|
||||||
|
Drop-in
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
rCSSmin effectively consists of two files: rcssmin.py and rcssmin.c, the
|
||||||
|
latter being entirely optional. So, for simple integration you can just
|
||||||
|
copy rcssmin.py into your project and use it.
|
||||||
|
|
||||||
|
|
||||||
|
DOCUMENTATION
|
||||||
|
-------------
|
||||||
|
|
||||||
|
A generated API documentation is available in the docs/apidoc/ directory.
|
||||||
|
But you can just look into the module. It provides a simple function,
|
||||||
|
called cssmin which takes the CSS as a string and returns the minified
|
||||||
|
CSS as a string.
|
||||||
|
|
||||||
|
The module additionally provides a "streamy" interface:
|
||||||
|
|
||||||
|
$ python -mrcssmin <css >minified
|
||||||
|
|
||||||
|
It takes two options:
|
||||||
|
|
||||||
|
-b Keep bang-comments (Comments starting with an exclamation mark)
|
||||||
|
-p Force using the python implementation (not the C implementation)
|
||||||
|
|
||||||
|
The latest documentation is also available online at
|
||||||
|
<http://opensource.perlig.de/rcssmin/>.
|
||||||
|
|
||||||
|
|
||||||
|
BUGS
|
||||||
|
----
|
||||||
|
|
||||||
|
No bugs, of course. ;-)
|
||||||
|
But if you've found one or have an idea how to improve rcssmin, feel free to
|
||||||
|
send a pull request on `github <https://github.com/ndparker/rcssmin>`_ or
|
||||||
|
send a mail to <rcssmin-bugs@perlig.de>.
|
||||||
|
|
||||||
|
|
||||||
|
AUTHOR INFORMATION
|
||||||
|
------------------
|
||||||
|
|
||||||
|
André "nd" Malo <nd@perlig.de>
|
||||||
|
GPG: 0x8103A37E
|
||||||
|
|
||||||
|
|
||||||
|
If God intended people to be naked, they would be born that way.
|
||||||
|
-- Oscar Wilde
|
25
_pkg/ebuilds/rcssmin.ebuild.in
Normal file
25
_pkg/ebuilds/rcssmin.ebuild.in
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Copyright 2011-2014 Andr\xe9 Malo
|
||||||
|
# Distributed under the terms of the GNU General Public License v2
|
||||||
|
# $Header$
|
||||||
|
|
||||||
|
EAPI="5"
|
||||||
|
PYTHON_COMPAT=( python{2_5,2_6,2_7,3_1,3_2,3_3} pypy{1_9,2_0} jython2_5 )
|
||||||
|
|
||||||
|
inherit distutils-r1
|
||||||
|
|
||||||
|
DESCRIPTION="Fast CSS minifier for python"
|
||||||
|
HOMEPAGE="http://opensource.perlig.de/rcssmin/"
|
||||||
|
SRC_URI="http://storage.perlig.de/rcssmin/${P}.tar.bz2"
|
||||||
|
RESTRICT="mirror"
|
||||||
|
|
||||||
|
LICENSE="Apache-2"
|
||||||
|
SLOT="0"
|
||||||
|
KEYWORDS="~amd64 ~ppc ~x86 ~x86-fbsd ~amd64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos"
|
||||||
|
IUSE="doc"
|
||||||
|
|
||||||
|
DOCS=( README.rst docs/CHANGES )
|
||||||
|
HTML_DOCS=( docs/apidoc )
|
||||||
|
|
||||||
|
python_install() {
|
||||||
|
distutils-r1_python_install --without-docs
|
||||||
|
}
|
243
_pkg/pylint.conf
Normal file
243
_pkg/pylint.conf
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
# pylint-version: 0.21.0
|
||||||
|
[MASTER]
|
||||||
|
|
||||||
|
# Specify a configuration file.
|
||||||
|
#rcfile=
|
||||||
|
|
||||||
|
# Python code to execute, usually for sys.path manipulation such as
|
||||||
|
# pygtk.require().
|
||||||
|
#init-hook=
|
||||||
|
|
||||||
|
# Profiled execution.
|
||||||
|
profile=no
|
||||||
|
|
||||||
|
# Add <file or directory> to the black list. It should be a base name, not a
|
||||||
|
# path. You may set this option multiple times.
|
||||||
|
ignore=
|
||||||
|
.git, .svn
|
||||||
|
|
||||||
|
# Pickle collected data for later comparisons.
|
||||||
|
persistent=no
|
||||||
|
|
||||||
|
# List of plugins (as comma separated values of python modules names) to load,
|
||||||
|
# usually to register additional checkers.
|
||||||
|
load-plugins=
|
||||||
|
|
||||||
|
|
||||||
|
[MESSAGES CONTROL]
|
||||||
|
|
||||||
|
# Enable the message, report, category or checker with the given id(s). You can
|
||||||
|
# either give multiple identifier separated by comma (,) or put this option
|
||||||
|
# multiple time.
|
||||||
|
#enable=
|
||||||
|
|
||||||
|
# Disable the message, report, category or checker with the given id(s). You
|
||||||
|
# can either give multiple identifier separated by comma (,) or put this option
|
||||||
|
# multiple time.
|
||||||
|
disable=
|
||||||
|
I0011,
|
||||||
|
W0142,
|
||||||
|
R0201, R0923,
|
||||||
|
|
||||||
|
|
||||||
|
[REPORTS]
|
||||||
|
|
||||||
|
# Set the output format. Available formats are text, parseable, colorized, msvs
|
||||||
|
# (visual studio) and html
|
||||||
|
output-format=parseable
|
||||||
|
|
||||||
|
# Include message's id in output
|
||||||
|
include-ids=yes
|
||||||
|
|
||||||
|
# Put messages in a separate file for each module / package specified on the
|
||||||
|
# command line instead of printing them on stdout. Reports (if any) will be
|
||||||
|
# written in a file name "pylint_global.[txt|html]".
|
||||||
|
files-output=no
|
||||||
|
|
||||||
|
# Tells whether to display a full report or only the messages
|
||||||
|
reports=no
|
||||||
|
|
||||||
|
# Python expression which should return a note less than 10 (10 is the highest
|
||||||
|
# note). You have access to the variables errors warning, statement which
|
||||||
|
# respectively contain the number of errors / warnings messages and the total
|
||||||
|
# number of statements analyzed. This is used by the global evaluation report
|
||||||
|
# (R0004).
|
||||||
|
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||||
|
|
||||||
|
# Add a comment according to your evaluation note. This is used by the global
|
||||||
|
# evaluation report (R0004).
|
||||||
|
comment=no
|
||||||
|
|
||||||
|
|
||||||
|
[FORMAT]
|
||||||
|
|
||||||
|
# Maximum number of characters on a single line.
|
||||||
|
max-line-length=78
|
||||||
|
|
||||||
|
# Maximum number of lines in a module
|
||||||
|
max-module-lines=1500
|
||||||
|
|
||||||
|
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||||
|
# tab).
|
||||||
|
indent-string=' '
|
||||||
|
|
||||||
|
|
||||||
|
[VARIABLES]
|
||||||
|
|
||||||
|
# Tells whether we should check for unused import in __init__ files.
|
||||||
|
init-import=yes
|
||||||
|
|
||||||
|
# A regular expression matching names used for dummy variables (i.e. not used).
|
||||||
|
dummy-variables-rgx=_|dummy|(?:kw)?args
|
||||||
|
|
||||||
|
# List of additional names supposed to be defined in builtins. Remember that
|
||||||
|
# you should avoid to define new builtins when possible.
|
||||||
|
additional-builtins=
|
||||||
|
|
||||||
|
|
||||||
|
[TYPECHECK]
|
||||||
|
|
||||||
|
# Tells whether missing members accessed in mixin class should be ignored. A
|
||||||
|
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||||
|
ignore-mixin-members=yes
|
||||||
|
|
||||||
|
# List of classes names for which member attributes should not be checked
|
||||||
|
# (useful for classes with attributes dynamically set).
|
||||||
|
ignored-classes=
|
||||||
|
|
||||||
|
# When zope mode is activated, add a predefined set of Zope acquired attributes
|
||||||
|
# to generated-members.
|
||||||
|
zope=no
|
||||||
|
|
||||||
|
# List of members which are set dynamically and missed by pylint inference
|
||||||
|
# system, and so shouldn't trigger E0201 when accessed.
|
||||||
|
generated-members=
|
||||||
|
|
||||||
|
|
||||||
|
[SIMILARITIES]
|
||||||
|
|
||||||
|
# Minimum lines number of a similarity.
|
||||||
|
min-similarity-lines=4
|
||||||
|
|
||||||
|
# Ignore comments when computing similarities.
|
||||||
|
ignore-comments=yes
|
||||||
|
|
||||||
|
# Ignore docstrings when computing similarities.
|
||||||
|
ignore-docstrings=yes
|
||||||
|
|
||||||
|
|
||||||
|
[MISCELLANEOUS]
|
||||||
|
|
||||||
|
# List of note tags to take in consideration, separated by a comma.
|
||||||
|
notes=FIXME,XXX,TODO
|
||||||
|
|
||||||
|
|
||||||
|
[BASIC]
|
||||||
|
|
||||||
|
# Required attributes for module, separated by a comma
|
||||||
|
required-attributes=
|
||||||
|
__author__, __docformat__
|
||||||
|
|
||||||
|
# List of builtins function names that should not be used, separated by a comma
|
||||||
|
bad-functions=
|
||||||
|
apply,input,exec,hasattr
|
||||||
|
|
||||||
|
# Regular expression which should only match correct module names
|
||||||
|
module-rgx=[a-z_][a-z0-9_]*$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct module level names
|
||||||
|
const-rgx=(?:_?[A-Z][A-Z0-9_]{1,30}|_?[A-Z][a-zA-Z0-9]{2,30}|_?[a-z_][a-z0-9_]{2,30}|__.*__)$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct class names
|
||||||
|
class-rgx=_?[A-Z][a-zA-Z0-9]+$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct function names
|
||||||
|
function-rgx=(?:__[a-z]+__|_?[a-z_][a-z0-9_]*)$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct method names
|
||||||
|
method-rgx=(?:__[a-z]+__|_?[a-z_][a-z0-9_]*)$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct instance attribute names
|
||||||
|
attr-rgx=(?:[A-Z][A-Z0-9_]{2,30}|[a-z_][a-z0-9_]{2,30})$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct argument names
|
||||||
|
argument-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct variable names
|
||||||
|
variable-rgx=(?:[A-Z_][A-Z0-9_]{1,30}|[a-z_][a-z0-9_]{2,30})$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct list comprehension /
|
||||||
|
# generator expression variable names
|
||||||
|
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]{,10}$
|
||||||
|
|
||||||
|
# Good variable names which should always be accepted, separated by a comma
|
||||||
|
good-names=_
|
||||||
|
|
||||||
|
# Bad variable names which should always be refused, separated by a comma
|
||||||
|
bad-names=foo,bar,baz,toto,tutu,tata
|
||||||
|
|
||||||
|
# Regular expression which should only match functions or classes name which do
|
||||||
|
# not require a docstring
|
||||||
|
no-docstring-rgx=__.*__
|
||||||
|
|
||||||
|
|
||||||
|
[DESIGN]
|
||||||
|
|
||||||
|
# Maximum number of arguments for function / method
|
||||||
|
max-args=10
|
||||||
|
|
||||||
|
# Argument names that match this expression will be ignored. Default to name
|
||||||
|
# with leading underscore
|
||||||
|
ignored-argument-names=_.*
|
||||||
|
|
||||||
|
# Maximum number of locals for function / method body
|
||||||
|
max-locals=20
|
||||||
|
|
||||||
|
# Maximum number of return / yield for function / method body
|
||||||
|
max-returns=6
|
||||||
|
|
||||||
|
# Maximum number of branch for function / method body
|
||||||
|
max-branchs=12
|
||||||
|
|
||||||
|
# Maximum number of statements in function / method body
|
||||||
|
max-statements=50
|
||||||
|
|
||||||
|
# Maximum number of parents for a class (see R0901).
|
||||||
|
max-parents=7
|
||||||
|
|
||||||
|
# Maximum number of attributes for a class (see R0902).
|
||||||
|
max-attributes=20
|
||||||
|
|
||||||
|
# Minimum number of public methods for a class (see R0903).
|
||||||
|
min-public-methods=0
|
||||||
|
|
||||||
|
# Maximum number of public methods for a class (see R0904).
|
||||||
|
max-public-methods=50
|
||||||
|
|
||||||
|
|
||||||
|
[IMPORTS]
|
||||||
|
|
||||||
|
# Deprecated modules which should not be used, separated by a comma
|
||||||
|
deprecated-modules=regsub,TERMIOS,Bastion,rexec
|
||||||
|
|
||||||
|
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||||
|
# given file (report R0402 must not be disabled)
|
||||||
|
import-graph=
|
||||||
|
|
||||||
|
# Create a graph of external dependencies in the given file (report R0402 must
|
||||||
|
# not be disabled)
|
||||||
|
ext-import-graph=
|
||||||
|
|
||||||
|
# Create a graph of internal dependencies in the given file (report R0402 must
|
||||||
|
# not be disabled)
|
||||||
|
int-import-graph=
|
||||||
|
|
||||||
|
|
||||||
|
[CLASSES]
|
||||||
|
|
||||||
|
# List of interface methods to ignore, separated by a comma. This is used for
|
||||||
|
# instance to not check methods defines in Zope's Interface base class.
|
||||||
|
ignore-iface-methods=
|
||||||
|
|
||||||
|
# List of method names used to declare (i.e. assign) instance attributes.
|
||||||
|
defining-attr-methods=__init__,__new__
|
39
_setup/__init__.py
Normal file
39
_setup/__init__.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
================
|
||||||
|
Package _setup
|
||||||
|
================
|
||||||
|
|
||||||
|
This package provides tools for main package setup.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import os as _os
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
if _sys.version_info[0] == 2:
|
||||||
|
__path__ = [_os.path.join(__path__[0], 'py2')]
|
||||||
|
__author__ = __author__.decode('latin-1')
|
||||||
|
elif _sys.version_info[0] == 3:
|
||||||
|
__path__ = [_os.path.join(__path__[0], 'py3')]
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Unsupported python version")
|
||||||
|
del _os, _sys
|
||||||
|
|
||||||
|
from _setup.setup import run # pylint: disable = W0611
|
244
_setup/include/cext.h
Normal file
244
_setup/include/cext.h
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, 2007, 2008, 2009, 2010, 2011
|
||||||
|
* Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* central naming stuff
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SETUP_CEXT_H
|
||||||
|
#define SETUP_CEXT_H
|
||||||
|
|
||||||
|
#ifndef EXT_MODULE
|
||||||
|
#error EXT_MODULE must be defined outside of this file (-DEXT_MODULE=...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* include core header files
|
||||||
|
*/
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
|
||||||
|
#include "Python.h"
|
||||||
|
#include "structmember.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* define our helper macros depending on the stuff above
|
||||||
|
*/
|
||||||
|
#define STRINGIFY(n) STRINGIFY_HELPER(n)
|
||||||
|
#define STRINGIFY_HELPER(n) #n
|
||||||
|
#define CONCATENATE(first, second) CONCATENATE_HELPER(first, second)
|
||||||
|
#define CONCATENATE_HELPER(first, second) first##second
|
||||||
|
|
||||||
|
#define EXT_MODULE_NAME STRINGIFY(EXT_MODULE)
|
||||||
|
#ifdef EXT_PACKAGE
|
||||||
|
#define EXT_PACKAGE_NAME STRINGIFY(EXT_PACKAGE)
|
||||||
|
#define EXT_MODULE_PATH EXT_PACKAGE_NAME "." EXT_MODULE_NAME
|
||||||
|
#else
|
||||||
|
#define EXT_PACKAGE_NAME ""
|
||||||
|
#define EXT_MODULE_PATH EXT_MODULE_NAME
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define EXT_DOCS_VAR CONCATENATE(var, CONCATENATE(EXT_MODULE, __doc__))
|
||||||
|
#define EXT_METHODS_VAR CONCATENATE(var, CONCATENATE(EXT_MODULE, _methods))
|
||||||
|
#define EXT_METHODS static PyMethodDef EXT_METHODS_VAR[]
|
||||||
|
|
||||||
|
#define EXT_DEFINE_VAR CONCATENATE(var, CONCATENATE(EXT_MODULE, _module))
|
||||||
|
|
||||||
|
/* Py3K Support */
|
||||||
|
#if PY_MAJOR_VERSION >= 3
|
||||||
|
|
||||||
|
#define EXT3
|
||||||
|
|
||||||
|
#ifndef PyMODINIT_FUNC
|
||||||
|
#define EXT_INIT_FUNC PyObject *CONCATENATE(PyInit_, EXT_MODULE)(void)
|
||||||
|
#else
|
||||||
|
#define EXT_INIT_FUNC PyMODINIT_FUNC CONCATENATE(PyInit_, EXT_MODULE)(void)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define EXT_DEFINE(name, methods, doc) \
|
||||||
|
static struct PyModuleDef EXT_DEFINE_VAR = { \
|
||||||
|
PyModuleDef_HEAD_INIT, \
|
||||||
|
name, \
|
||||||
|
doc, \
|
||||||
|
-1, \
|
||||||
|
methods, \
|
||||||
|
NULL, \
|
||||||
|
NULL, \
|
||||||
|
NULL, \
|
||||||
|
NULL \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define EXT_CREATE(def) (PyModule_Create(def))
|
||||||
|
#define EXT_INIT_ERROR(module) do {Py_XDECREF(module); return NULL;} while(0)
|
||||||
|
#define EXT_INIT_RETURN(module) return module
|
||||||
|
|
||||||
|
#else /* end py3k */
|
||||||
|
|
||||||
|
#define EXT2
|
||||||
|
|
||||||
|
#ifndef PyMODINIT_FUNC
|
||||||
|
#define EXT_INIT_FUNC void CONCATENATE(init, EXT_MODULE)(void)
|
||||||
|
#else
|
||||||
|
#define EXT_INIT_FUNC PyMODINIT_FUNC CONCATENATE(init, EXT_MODULE)(void)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define EXT_DEFINE__STRUCT \
|
||||||
|
CONCATENATE(struct, CONCATENATE(EXT_MODULE, _module))
|
||||||
|
|
||||||
|
struct EXT_DEFINE__STRUCT {
|
||||||
|
char *m_name;
|
||||||
|
char *m_doc;
|
||||||
|
PyMethodDef *m_methods;
|
||||||
|
};
|
||||||
|
#define EXT_DEFINE(name, methods, doc) \
|
||||||
|
static struct EXT_DEFINE__STRUCT EXT_DEFINE_VAR = { \
|
||||||
|
name, \
|
||||||
|
doc, \
|
||||||
|
methods \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define EXT_CREATE(def) ((def)->m_doc \
|
||||||
|
? Py_InitModule3((def)->m_name, (def)->m_methods, (def)->m_doc) \
|
||||||
|
: Py_InitModule((def)->m_name, (def)->m_methods) \
|
||||||
|
)
|
||||||
|
#define EXT_INIT_ERROR(module) return
|
||||||
|
#define EXT_INIT_RETURN(module) return
|
||||||
|
|
||||||
|
#endif /* end py2K */
|
||||||
|
|
||||||
|
#define EXT_INIT_TYPE(module, type) do { \
|
||||||
|
if (PyType_Ready(type) < 0) \
|
||||||
|
EXT_INIT_ERROR(module); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define EXT_ADD_TYPE(module, name, type) do { \
|
||||||
|
Py_INCREF(type); \
|
||||||
|
if (PyModule_AddObject(module, name, (PyObject *)(type)) < 0) \
|
||||||
|
EXT_INIT_ERROR(module); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define EXT_ADD_UNICODE(module, name, string, encoding) do { \
|
||||||
|
if (PyModule_AddObject( \
|
||||||
|
module, \
|
||||||
|
name, \
|
||||||
|
PyUnicode_Decode( \
|
||||||
|
string, \
|
||||||
|
sizeof(string) - 1, \
|
||||||
|
encoding, \
|
||||||
|
"strict" \
|
||||||
|
)) < 0) \
|
||||||
|
EXT_INIT_ERROR(module); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define EXT_ADD_STRING(module, name, string) do { \
|
||||||
|
if (PyModule_AddStringConstant(module, name, string) < 0) \
|
||||||
|
EXT_INIT_ERROR(module); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define EXT_ADD_INT(module, name, number) do { \
|
||||||
|
if (PyModule_AddIntConstant(module, name, number) < 0) \
|
||||||
|
EXT_INIT_ERROR(module); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
/* PEP 353 support, implemented as of python 2.5 */
|
||||||
|
#if PY_VERSION_HEX < 0x02050000
|
||||||
|
typedef int Py_ssize_t;
|
||||||
|
#define PyInt_FromSsize_t(arg) PyInt_FromLong((long)arg)
|
||||||
|
#define PyInt_AsSsize_t(arg) (int)PyInt_AsLong(arg)
|
||||||
|
#define PY_SSIZE_T_MAX ((Py_ssize_t)INT_MAX)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* some helper macros (Python 2.4)
|
||||||
|
*/
|
||||||
|
#ifndef Py_VISIT
|
||||||
|
#define Py_VISIT(op) do { \
|
||||||
|
if (op) { \
|
||||||
|
int vret = visit((op), arg); \
|
||||||
|
if (vret) return vret; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Py_CLEAR
|
||||||
|
#undef Py_CLEAR
|
||||||
|
#endif
|
||||||
|
#define Py_CLEAR(op) do { \
|
||||||
|
if (op) { \
|
||||||
|
PyObject *tmp__ = (PyObject *)(op); \
|
||||||
|
(op) = NULL; \
|
||||||
|
Py_DECREF(tmp__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#ifndef Py_RETURN_NONE
|
||||||
|
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef Py_RETURN_FALSE
|
||||||
|
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef Py_RETURN_TRUE
|
||||||
|
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Macros for inline documentation. (Python 2.3) */
|
||||||
|
#ifndef PyDoc_VAR
|
||||||
|
#define PyDoc_VAR(name) static char name[]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PyDoc_STRVAR
|
||||||
|
#define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PyDoc_STR
|
||||||
|
#ifdef WITH_DOC_STRINGS
|
||||||
|
#define PyDoc_STR(str) str
|
||||||
|
#else
|
||||||
|
#define PyDoc_STR(str) ""
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Basestring check (basestring introduced in Python 2.3) */
|
||||||
|
#if PY_VERSION_HEX < 0x02030000
|
||||||
|
#define BaseString_Check(type) ( \
|
||||||
|
PyObject_TypeCheck((type), &PyString_Type) \
|
||||||
|
|| PyObject_TypeCheck((type), &PyUnicode_Type) \
|
||||||
|
)
|
||||||
|
#else
|
||||||
|
#define BaseString_Check(type) PyObject_TypeCheck((type), &PyBaseString_Type)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define GENERIC_ALLOC(type) \
|
||||||
|
((void *)((PyTypeObject *)type)->tp_alloc(type, (Py_ssize_t)0))
|
||||||
|
|
||||||
|
/* PyPy doesn't define it */
|
||||||
|
#ifndef PyType_IS_GC
|
||||||
|
#define PyType_IS_GC(t) PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DEFINE_GENERIC_DEALLOC(prefix) \
|
||||||
|
static void prefix##_dealloc(void *self) \
|
||||||
|
{ \
|
||||||
|
if (PyType_IS_GC(((PyObject *)self)->ob_type)) \
|
||||||
|
PyObject_GC_UnTrack(self); \
|
||||||
|
(void)prefix##_clear(self); \
|
||||||
|
((PyObject *)self)->ob_type->tp_free((PyObject *)self); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* SETUP_CEXT_H */
|
27
_setup/py2/__init__.py
Normal file
27
_setup/py2/__init__.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
================
|
||||||
|
Package _setup
|
||||||
|
================
|
||||||
|
|
||||||
|
This package provides tools for main package setup.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
from _setup.setup import run # pylint: disable = W0611
|
267
_setup/py2/commands.py
Normal file
267
_setup/py2/commands.py
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
===================
|
||||||
|
Command extenders
|
||||||
|
===================
|
||||||
|
|
||||||
|
Command extenders.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
__test__ = False
|
||||||
|
|
||||||
|
from distutils import fancy_getopt as _fancy_getopt
|
||||||
|
from distutils.command import build as _build
|
||||||
|
from distutils.command import build_ext as _build_ext
|
||||||
|
from distutils.command import install as _install
|
||||||
|
from distutils.command import install_data as _install_data
|
||||||
|
from distutils.command import install_lib as _install_lib
|
||||||
|
import os as _os
|
||||||
|
|
||||||
|
from _setup.util import log
|
||||||
|
|
||||||
|
_option_defaults = {}
|
||||||
|
_option_inherits = {}
|
||||||
|
_option_finalizers = {}
|
||||||
|
_command_mapping = {
|
||||||
|
'install': 'Install',
|
||||||
|
'install_data': 'InstallData',
|
||||||
|
'install_lib': 'InstallLib',
|
||||||
|
'build': 'Build',
|
||||||
|
'build_ext': 'BuildExt',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def add_option(command, long_name, help_text, short_name=None, default=None,
|
||||||
|
inherit=None):
|
||||||
|
""" Add an option """
|
||||||
|
try:
|
||||||
|
command_class = globals()[_command_mapping[command]]
|
||||||
|
except KeyError:
|
||||||
|
raise ValueError("Unknown command %r" % (command,))
|
||||||
|
for opt in command_class.user_options:
|
||||||
|
if opt[0] == long_name:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
opt = (long_name, short_name, help_text)
|
||||||
|
command_class.user_options.append(opt)
|
||||||
|
if not long_name.endswith('='):
|
||||||
|
command_class.boolean_options.append(long_name)
|
||||||
|
attr_name = _fancy_getopt.translate_longopt(long_name)
|
||||||
|
else:
|
||||||
|
attr_name = _fancy_getopt.translate_longopt(long_name[:-1])
|
||||||
|
if not _option_defaults.has_key(command):
|
||||||
|
_option_defaults[command] = []
|
||||||
|
if inherit is not None:
|
||||||
|
if isinstance(inherit, (str, unicode)):
|
||||||
|
inherit = [inherit]
|
||||||
|
for i_inherit in inherit:
|
||||||
|
add_option(
|
||||||
|
i_inherit, long_name, help_text, short_name, default
|
||||||
|
)
|
||||||
|
default = None
|
||||||
|
if not _option_inherits.has_key(command):
|
||||||
|
_option_inherits[command] = []
|
||||||
|
for i_inherit in inherit:
|
||||||
|
for i_command, opt_name in _option_inherits[command]:
|
||||||
|
if i_command == i_inherit and opt_name == attr_name:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
_option_inherits[command].append((i_inherit, attr_name))
|
||||||
|
_option_defaults[command].append((attr_name, default))
|
||||||
|
|
||||||
|
|
||||||
|
def add_finalizer(command, key, func):
|
||||||
|
""" Add finalizer """
|
||||||
|
if not _option_finalizers.has_key(command):
|
||||||
|
_option_finalizers[command] = {}
|
||||||
|
if not _option_finalizers[command].has_key(key):
|
||||||
|
_option_finalizers[command][key] = func
|
||||||
|
|
||||||
|
|
||||||
|
class Install(_install.install):
|
||||||
|
""" Extended installer to reflect the additional data options """
|
||||||
|
user_options = _install.install.user_options + [
|
||||||
|
('single-version-externally-managed', None,
|
||||||
|
"Compat option. Does not a thing."),
|
||||||
|
]
|
||||||
|
boolean_options = _install.install.boolean_options + [
|
||||||
|
'single-version-externally-managed'
|
||||||
|
]
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
""" Prepare for new options """
|
||||||
|
_install.install.initialize_options(self)
|
||||||
|
self.single_version_externally_managed = None
|
||||||
|
if _option_defaults.has_key('install'):
|
||||||
|
for opt_name, default in _option_defaults['install']:
|
||||||
|
setattr(self, opt_name, default)
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
""" Finalize options """
|
||||||
|
_install.install.finalize_options(self)
|
||||||
|
if _option_inherits.has_key('install'):
|
||||||
|
for parent, opt_name in _option_inherits['install']:
|
||||||
|
self.set_undefined_options(parent, (opt_name, opt_name))
|
||||||
|
if _option_finalizers.has_key('install'):
|
||||||
|
for func in _option_finalizers['install'].values():
|
||||||
|
func(self)
|
||||||
|
|
||||||
|
|
||||||
|
class InstallData(_install_data.install_data):
|
||||||
|
""" Extended data installer """
|
||||||
|
user_options = _install_data.install_data.user_options + []
|
||||||
|
boolean_options = _install_data.install_data.boolean_options + []
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
""" Prepare for new options """
|
||||||
|
_install_data.install_data.initialize_options(self)
|
||||||
|
if _option_defaults.has_key('install_data'):
|
||||||
|
for opt_name, default in _option_defaults['install_data']:
|
||||||
|
setattr(self, opt_name, default)
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
""" Finalize options """
|
||||||
|
_install_data.install_data.finalize_options(self)
|
||||||
|
if _option_inherits.has_key('install_data'):
|
||||||
|
for parent, opt_name in _option_inherits['install_data']:
|
||||||
|
self.set_undefined_options(parent, (opt_name, opt_name))
|
||||||
|
if _option_finalizers.has_key('install_data'):
|
||||||
|
for func in _option_finalizers['install_data'].values():
|
||||||
|
func(self)
|
||||||
|
|
||||||
|
|
||||||
|
class InstallLib(_install_lib.install_lib):
|
||||||
|
""" Extended lib installer """
|
||||||
|
user_options = _install_lib.install_lib.user_options + []
|
||||||
|
boolean_options = _install_lib.install_lib.boolean_options + []
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
""" Prepare for new options """
|
||||||
|
_install_lib.install_lib.initialize_options(self)
|
||||||
|
if _option_defaults.has_key('install_lib'):
|
||||||
|
for opt_name, default in _option_defaults['install_lib']:
|
||||||
|
setattr(self, opt_name, default)
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
""" Finalize options """
|
||||||
|
_install_lib.install_lib.finalize_options(self)
|
||||||
|
if _option_inherits.has_key('install_lib'):
|
||||||
|
for parent, opt_name in _option_inherits['install_lib']:
|
||||||
|
self.set_undefined_options(parent, (opt_name, opt_name))
|
||||||
|
if _option_finalizers.has_key('install_lib'):
|
||||||
|
for func in _option_finalizers['install_lib'].values():
|
||||||
|
func(self)
|
||||||
|
|
||||||
|
|
||||||
|
class BuildExt(_build_ext.build_ext):
|
||||||
|
"""
|
||||||
|
Extended extension builder class
|
||||||
|
|
||||||
|
This class allows extensions to provide a ``check_prerequisites`` method
|
||||||
|
which is called before actually building it. The method takes the
|
||||||
|
`BuildExt` instance and returns whether the extension should be skipped or
|
||||||
|
not.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
""" Prepare for new options """
|
||||||
|
_build_ext.build_ext.initialize_options(self)
|
||||||
|
if _option_defaults.has_key('build_ext'):
|
||||||
|
for opt_name, default in _option_defaults['build_ext']:
|
||||||
|
setattr(self, opt_name, default)
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
""" Finalize options """
|
||||||
|
_build_ext.build_ext.finalize_options(self)
|
||||||
|
if _option_inherits.has_key('build_ext'):
|
||||||
|
for parent, opt_name in _option_inherits['build_ext']:
|
||||||
|
self.set_undefined_options(parent, (opt_name, opt_name))
|
||||||
|
if _option_finalizers.has_key('build_ext'):
|
||||||
|
for func in _option_finalizers['build_ext'].values():
|
||||||
|
func(self)
|
||||||
|
|
||||||
|
def build_extension(self, ext):
|
||||||
|
"""
|
||||||
|
Build C extension - with extended functionality
|
||||||
|
|
||||||
|
The following features are added here:
|
||||||
|
|
||||||
|
- ``ext.check_prerequisites`` is called before the extension is being
|
||||||
|
built. See `Extension` for details. If the method does not exist,
|
||||||
|
simply no check will be run.
|
||||||
|
- The macros ``EXT_PACKAGE`` and ``EXT_MODULE`` will be filled (or
|
||||||
|
unset) depending on the extensions name, but only if they are not
|
||||||
|
already defined.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`ext` : `Extension`
|
||||||
|
The extension to build. If it's a pure
|
||||||
|
``distutils.core.Extension``, simply no prequisites check is
|
||||||
|
applied.
|
||||||
|
|
||||||
|
:Return: whatever ``distutils.command.build_ext.build_ext`` returns
|
||||||
|
:Rtype: any
|
||||||
|
"""
|
||||||
|
# handle name macros
|
||||||
|
macros = dict(ext.define_macros or ())
|
||||||
|
tup = ext.name.split('.')
|
||||||
|
if len(tup) == 1:
|
||||||
|
pkg, mod = None, tup[0]
|
||||||
|
else:
|
||||||
|
pkg, mod = '.'.join(tup[:-1]), tup[-1]
|
||||||
|
if pkg is not None and 'EXT_PACKAGE' not in macros:
|
||||||
|
ext.define_macros.append(('EXT_PACKAGE', pkg))
|
||||||
|
if 'EXT_MODULE' not in macros:
|
||||||
|
ext.define_macros.append(('EXT_MODULE', mod))
|
||||||
|
if pkg is None:
|
||||||
|
macros = dict(ext.undef_macros or ())
|
||||||
|
if 'EXT_PACKAGE' not in macros:
|
||||||
|
ext.undef_macros.append('EXT_PACKAGE')
|
||||||
|
|
||||||
|
# handle prereq checks
|
||||||
|
try:
|
||||||
|
checker = ext.check_prerequisites
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if checker(self):
|
||||||
|
log.info("Skipping %s extension" % ext.name)
|
||||||
|
return
|
||||||
|
|
||||||
|
return _build_ext.build_ext.build_extension(self, ext)
|
||||||
|
|
||||||
|
|
||||||
|
class Build(_build.build):
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
""" Prepare for new options """
|
||||||
|
_build.build.initialize_options(self)
|
||||||
|
if _option_defaults.has_key('build'):
|
||||||
|
for opt_name, default in _option_defaults['build']:
|
||||||
|
setattr(self, opt_name, default)
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
""" Finalize options """
|
||||||
|
_build.build.finalize_options(self)
|
||||||
|
if _option_inherits.has_key('build'):
|
||||||
|
for parent, opt_name in _option_inherits['build']:
|
||||||
|
self.set_undefined_options(parent, (opt_name, opt_name))
|
||||||
|
if _option_finalizers.has_key('build'):
|
||||||
|
for func in _option_finalizers['build'].values():
|
||||||
|
func(self)
|
165
_setup/py2/data.py
Normal file
165
_setup/py2/data.py
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
===================
|
||||||
|
Data distribution
|
||||||
|
===================
|
||||||
|
|
||||||
|
This module provides tools to simplify data distribution.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
from distutils import filelist as _filelist
|
||||||
|
import os as _os
|
||||||
|
import posixpath as _posixpath
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import commands as _commands
|
||||||
|
|
||||||
|
|
||||||
|
def splitpath(path):
|
||||||
|
""" Split a path """
|
||||||
|
drive, path = '', _os.path.normpath(path)
|
||||||
|
try:
|
||||||
|
splitunc = _os.path.splitunc
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
drive, path = splitunc(path)
|
||||||
|
if not drive:
|
||||||
|
drive, path = _os.path.splitdrive(path)
|
||||||
|
elems = []
|
||||||
|
try:
|
||||||
|
sep = _os.path.sep
|
||||||
|
except AttributeError:
|
||||||
|
sep = _os.path.join('1', '2')[1:-1]
|
||||||
|
while 1:
|
||||||
|
prefix, path = _os.path.split(path)
|
||||||
|
elems.append(path)
|
||||||
|
if prefix in ('', sep):
|
||||||
|
drive = _os.path.join(drive, prefix)
|
||||||
|
break
|
||||||
|
path = prefix
|
||||||
|
elems.reverse()
|
||||||
|
return drive, elems
|
||||||
|
|
||||||
|
|
||||||
|
def finalizer(installer):
|
||||||
|
""" Finalize install_data """
|
||||||
|
data_files = []
|
||||||
|
for item in installer.data_files:
|
||||||
|
if not isinstance(item, Data):
|
||||||
|
data_files.append(item)
|
||||||
|
continue
|
||||||
|
data_files.extend(item.flatten(installer))
|
||||||
|
installer.data_files = data_files
|
||||||
|
|
||||||
|
|
||||||
|
class Data(object):
|
||||||
|
""" File list container """
|
||||||
|
|
||||||
|
def __init__(self, files, target=None, preserve=0, strip=0,
|
||||||
|
prefix=None):
|
||||||
|
""" Initialization """
|
||||||
|
self._files = files
|
||||||
|
self._target = target
|
||||||
|
self._preserve = preserve
|
||||||
|
self._strip = strip
|
||||||
|
self._prefix = prefix
|
||||||
|
self.fixup_commands()
|
||||||
|
|
||||||
|
def fixup_commands(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def from_templates(cls, *templates, **kwargs):
|
||||||
|
""" Initialize from template """
|
||||||
|
files = _filelist.FileList()
|
||||||
|
for tpl in templates:
|
||||||
|
for line in tpl.split(';'):
|
||||||
|
files.process_template_line(line.strip())
|
||||||
|
files.sort()
|
||||||
|
files.remove_duplicates()
|
||||||
|
result = []
|
||||||
|
for filename in files.files:
|
||||||
|
_, elems = splitpath(filename)
|
||||||
|
if '.svn' in elems or '.git' in elems:
|
||||||
|
continue
|
||||||
|
result.append(filename)
|
||||||
|
return cls(result, **kwargs)
|
||||||
|
from_templates = classmethod(from_templates)
|
||||||
|
|
||||||
|
def flatten(self, installer):
|
||||||
|
""" Flatten the file list to (target, file) tuples """
|
||||||
|
# pylint: disable = W0613
|
||||||
|
if self._prefix:
|
||||||
|
_, prefix = splitpath(self._prefix)
|
||||||
|
telems = prefix
|
||||||
|
else:
|
||||||
|
telems = []
|
||||||
|
|
||||||
|
tmap = {}
|
||||||
|
for fname in self._files:
|
||||||
|
(_, name), target = splitpath(fname), telems
|
||||||
|
if self._preserve:
|
||||||
|
if self._strip:
|
||||||
|
name = name[max(0, min(self._strip, len(name) - 1)):]
|
||||||
|
if len(name) > 1:
|
||||||
|
target = telems + name[:-1]
|
||||||
|
tmap.setdefault(_posixpath.join(*target), []).append(fname)
|
||||||
|
return tmap.items()
|
||||||
|
|
||||||
|
|
||||||
|
class Documentation(Data):
|
||||||
|
""" Documentation container """
|
||||||
|
|
||||||
|
def fixup_commands(self):
|
||||||
|
_commands.add_option('install_data', 'without-docs',
|
||||||
|
help_text='Do not install documentation files',
|
||||||
|
inherit='install',
|
||||||
|
)
|
||||||
|
_commands.add_finalizer('install_data', 'documentation', finalizer)
|
||||||
|
|
||||||
|
def flatten(self, installer):
|
||||||
|
""" Check if docs should be installed at all """
|
||||||
|
if installer.without_docs:
|
||||||
|
return []
|
||||||
|
return Data.flatten(self, installer)
|
||||||
|
|
||||||
|
|
||||||
|
class Manpages(Documentation):
|
||||||
|
""" Manpages container """
|
||||||
|
|
||||||
|
def dispatch(cls, files):
|
||||||
|
""" Automatically dispatch manpages to their target directories """
|
||||||
|
mpmap = {}
|
||||||
|
for manpage in files:
|
||||||
|
normalized = _os.path.normpath(manpage)
|
||||||
|
_, ext = _os.path.splitext(normalized)
|
||||||
|
if ext.startswith(_os.path.extsep):
|
||||||
|
ext = ext[len(_os.path.extsep):]
|
||||||
|
mpmap.setdefault(ext, []).append(manpage)
|
||||||
|
return [cls(manpages, prefix=_posixpath.join(
|
||||||
|
'share', 'man', 'man%s' % section,
|
||||||
|
)) for section, manpages in mpmap.items()]
|
||||||
|
dispatch = classmethod(dispatch)
|
||||||
|
|
||||||
|
def flatten(self, installer):
|
||||||
|
""" Check if manpages are suitable """
|
||||||
|
if _sys.platform == 'win32':
|
||||||
|
return []
|
||||||
|
return Documentation.flatten(self, installer)
|
25
_setup/py2/dev/__init__.py
Normal file
25
_setup/py2/dev/__init__.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
====================
|
||||||
|
Package _setup.dev
|
||||||
|
====================
|
||||||
|
|
||||||
|
Development tools, not distributed.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
258
_setup/py2/dev/_pylint.py
Normal file
258
_setup/py2/dev/_pylint.py
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2006, 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=================================
|
||||||
|
Support for code analysis tools
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Support for code analysis tools.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import re as _re
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import term as _term
|
||||||
|
from _setup import shell as _shell
|
||||||
|
|
||||||
|
|
||||||
|
class NotFinished(Exception):
|
||||||
|
""" Exception used for message passing in the stream filter """
|
||||||
|
|
||||||
|
class NotParseable(Exception):
|
||||||
|
""" Exception used for message passing in the stream filter """
|
||||||
|
|
||||||
|
class SpecialMessage(Exception):
|
||||||
|
""" Exception used for message passing in the stream filter """
|
||||||
|
|
||||||
|
|
||||||
|
class FilterStream(object):
|
||||||
|
""" Stream filter """
|
||||||
|
_LINERE = _re.compile(r'''
|
||||||
|
(?P<name>[^:]+)
|
||||||
|
:
|
||||||
|
(?P<lineno>\d+)
|
||||||
|
:\s+
|
||||||
|
\[(?P<mid>[^\],]+)(?:,\s+(?P<func>[^\]]+))?\]
|
||||||
|
\s+
|
||||||
|
(?P<desc>.*)
|
||||||
|
''', _re.X)
|
||||||
|
_SIMRE = _re.compile(r'in (?P<number>\d+) files')
|
||||||
|
_CYCRE = _re.compile(r'\((?P<cycle>[^)]+)\)')
|
||||||
|
|
||||||
|
def __init__(self, term, stream=_sys.stdout):
|
||||||
|
self.written = False
|
||||||
|
self._stream = stream
|
||||||
|
self._lastname = None
|
||||||
|
self._cycled = False
|
||||||
|
self._term = dict(term)
|
||||||
|
self._buffer = ''
|
||||||
|
|
||||||
|
def write(self, towrite):
|
||||||
|
""" Stream write function """
|
||||||
|
self._buffer += towrite
|
||||||
|
term = self._term
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
name, lineno, mid, func, desc = self._parse()
|
||||||
|
except NotFinished:
|
||||||
|
break
|
||||||
|
except SpecialMessage, e:
|
||||||
|
self._dospecial(e)
|
||||||
|
continue
|
||||||
|
except NotParseable, e:
|
||||||
|
self._print_literal(str(e.args[0]))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if name != self._lastname:
|
||||||
|
if self._lastname is not None:
|
||||||
|
self._stream.write("\n")
|
||||||
|
term['path'] = name
|
||||||
|
self._stream.write(
|
||||||
|
"%(BOLD)s>>> %(path)s%(NORMAL)s\n" % term
|
||||||
|
)
|
||||||
|
self._lastname = name
|
||||||
|
self.written = True
|
||||||
|
|
||||||
|
term['mid'] = mid
|
||||||
|
if mid.startswith('E') or mid.startswith('F'):
|
||||||
|
self._stream.write("%(BOLD)s%(RED)s%(mid)s%(NORMAL)s" % term)
|
||||||
|
elif mid == 'W0511':
|
||||||
|
self._stream.write(
|
||||||
|
"%(BOLD)s%(GREEN)s%(mid)s%(NORMAL)s" % term
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._stream.write(
|
||||||
|
"%(BOLD)s%(YELLOW)s%(mid)s%(NORMAL)s" % term
|
||||||
|
)
|
||||||
|
|
||||||
|
if int(lineno) != 0:
|
||||||
|
term['lineno'] = lineno
|
||||||
|
self._stream.write(" (%(lineno)s" % term)
|
||||||
|
if func:
|
||||||
|
term['func'] = func
|
||||||
|
self._stream.write(
|
||||||
|
", %(BOLD)s%(YELLOW)s%(func)s%(NORMAL)s" % term
|
||||||
|
)
|
||||||
|
self._stream.write(')')
|
||||||
|
|
||||||
|
self._stream.write(": %s\n" % desc)
|
||||||
|
self._stream.flush()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _print_literal(self, line):
|
||||||
|
""" Print literal """
|
||||||
|
suppress = (
|
||||||
|
line.startswith('Unable to get imported names for ') or
|
||||||
|
line.startswith("Exception exceptions.RuntimeError: 'generator "
|
||||||
|
"ignored GeneratorExit' in <generator object at") or
|
||||||
|
line.startswith("Exception RuntimeError: 'generator "
|
||||||
|
"ignored GeneratorExit' in <generator object") or
|
||||||
|
not line.strip()
|
||||||
|
)
|
||||||
|
if not suppress:
|
||||||
|
self._stream.write("%s\n" % line)
|
||||||
|
self._stream.flush()
|
||||||
|
self.written = True
|
||||||
|
|
||||||
|
def _dospecial(self, e):
|
||||||
|
""" Deal with special messages """
|
||||||
|
if e.args[0] == 'R0401':
|
||||||
|
pos = self._buffer.find('\n')
|
||||||
|
line, self._buffer = (
|
||||||
|
self._buffer[:pos + 1], self._buffer[pos + 1:]
|
||||||
|
)
|
||||||
|
term = self._term
|
||||||
|
term['mid'] = e.args[0]
|
||||||
|
if not self._cycled:
|
||||||
|
self._cycled = True
|
||||||
|
self._stream.write('\n')
|
||||||
|
self._stream.write(
|
||||||
|
"%(BOLD)s%(YELLOW)s%(mid)s%(NORMAL)s" % term
|
||||||
|
)
|
||||||
|
self._stream.write(": Cyclic imports\n")
|
||||||
|
match = self._CYCRE.search(e.args[1])
|
||||||
|
term['cycle'] = match.group('cycle')
|
||||||
|
self._stream.write("%(BOLD)s@@@ %(NORMAL)s%(cycle)s\n" % term)
|
||||||
|
self._stream.flush()
|
||||||
|
self.written = True
|
||||||
|
|
||||||
|
elif e.args[0] == 'R0801':
|
||||||
|
match = self._SIMRE.search(e.args[1])
|
||||||
|
if not match:
|
||||||
|
raise AssertionError(
|
||||||
|
'Could not determine number of similar files'
|
||||||
|
)
|
||||||
|
|
||||||
|
numfiles = int(match.group('number'))
|
||||||
|
pos = -1
|
||||||
|
for _ in range(numfiles + 1):
|
||||||
|
pos = self._buffer.find('\n', pos + 1)
|
||||||
|
if pos >= 0:
|
||||||
|
lines = self._buffer[:pos + 1]
|
||||||
|
self._buffer = self._buffer[pos + 1:]
|
||||||
|
term = self._term
|
||||||
|
|
||||||
|
self._stream.write("\n")
|
||||||
|
for name in lines.splitlines()[1:]:
|
||||||
|
name = name.rstrip()[2:]
|
||||||
|
term['path'] = name
|
||||||
|
self._stream.write(
|
||||||
|
"%(BOLD)s=== %(path)s%(NORMAL)s\n" % term
|
||||||
|
)
|
||||||
|
self._lastname = name
|
||||||
|
|
||||||
|
term['mid'] = e.args[0]
|
||||||
|
self._stream.write(
|
||||||
|
"%(BOLD)s%(YELLOW)s%(mid)s%(NORMAL)s" % term
|
||||||
|
)
|
||||||
|
self._stream.write(": %s\n" % e.args[1])
|
||||||
|
self._stream.flush()
|
||||||
|
self.written = True
|
||||||
|
|
||||||
|
def _parse(self):
|
||||||
|
""" Parse output """
|
||||||
|
if '\n' not in self._buffer:
|
||||||
|
raise NotFinished()
|
||||||
|
|
||||||
|
line = self._buffer[:self._buffer.find('\n') + 1]
|
||||||
|
self._buffer = self._buffer[len(line):]
|
||||||
|
line = line.rstrip()
|
||||||
|
match = self._LINERE.match(line)
|
||||||
|
if not match:
|
||||||
|
raise NotParseable(line)
|
||||||
|
|
||||||
|
mid = match.group('mid')
|
||||||
|
if mid in ('R0801', 'R0401'):
|
||||||
|
self._buffer = "%s\n%s" % (line, self._buffer)
|
||||||
|
raise SpecialMessage(mid, match.group('desc'))
|
||||||
|
|
||||||
|
return match.group('name', 'lineno', 'mid', 'func', 'desc')
|
||||||
|
|
||||||
|
|
||||||
|
def run(config, *args):
|
||||||
|
""" Run pylint """
|
||||||
|
try:
|
||||||
|
from pylint import lint
|
||||||
|
from pylint.reporters import text
|
||||||
|
except ImportError:
|
||||||
|
return 2
|
||||||
|
|
||||||
|
if config is None:
|
||||||
|
config = _shell.native('pylint.conf')
|
||||||
|
argv = ['--rcfile', config,
|
||||||
|
'--reports', 'no',
|
||||||
|
'--output-format', 'parseable',
|
||||||
|
'--include-ids', 'yes'
|
||||||
|
]
|
||||||
|
|
||||||
|
stream = FilterStream(_term.terminfo())
|
||||||
|
|
||||||
|
old_stderr = _sys.stderr
|
||||||
|
try:
|
||||||
|
# pylint: disable = E1101
|
||||||
|
_sys.stderr = stream
|
||||||
|
from pylint import __pkginfo__
|
||||||
|
if __pkginfo__.numversion < (0, 13):
|
||||||
|
# The lint tool is not very user friendly, so we need a hack here.
|
||||||
|
lint.REPORTER_OPT_MAP['parseable'] = \
|
||||||
|
lambda: text.TextReporter2(stream)
|
||||||
|
reporter = text.TextReporter2(stream)
|
||||||
|
else:
|
||||||
|
reporter = text.ParseableTextReporter(stream)
|
||||||
|
lint.REPORTER_OPT_MAP['parseable'] = lambda: reporter
|
||||||
|
|
||||||
|
for path in args:
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
lint.Run(argv + [path], reporter=reporter)
|
||||||
|
except SystemExit:
|
||||||
|
pass # don't accept the exit. strange errors happen...
|
||||||
|
|
||||||
|
if stream.written:
|
||||||
|
print
|
||||||
|
stream.written = False
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
_sys.stderr = old_stderr
|
||||||
|
|
||||||
|
return 0
|
31
_setup/py2/dev/analysis.py
Normal file
31
_setup/py2/dev/analysis.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2006, 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=================================
|
||||||
|
Support for code analysis tools
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Support for code analysis tools.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
|
||||||
|
def pylint(config, *args):
|
||||||
|
""" Run pylint """
|
||||||
|
from _setup.dev import _pylint
|
||||||
|
return _pylint.run(config, *args)
|
131
_setup/py2/dev/apidoc.py
Normal file
131
_setup/py2/dev/apidoc.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
==================
|
||||||
|
API doc builders
|
||||||
|
==================
|
||||||
|
|
||||||
|
API doc builders.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import os as _os
|
||||||
|
import re as _re
|
||||||
|
|
||||||
|
from _setup import shell as _shell
|
||||||
|
from _setup import term as _term
|
||||||
|
from _setup import util as _util
|
||||||
|
|
||||||
|
|
||||||
|
def _cleanup_epydoc(target):
|
||||||
|
"""
|
||||||
|
Cleanup epydoc generated files
|
||||||
|
|
||||||
|
This removes the epydoc-footer. It changes every release because of the
|
||||||
|
timestamp. That creates bad diffs (accidently it's also invalid html).
|
||||||
|
"""
|
||||||
|
search = _re.compile(r'<table[^<>]+width="100%%"').search
|
||||||
|
for filename in _shell.files(target, '*.html'):
|
||||||
|
fp = open(filename, 'r')
|
||||||
|
try:
|
||||||
|
html = fp.read()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
match = search(html)
|
||||||
|
if match:
|
||||||
|
start = match.start()
|
||||||
|
end = html.find('</table>', start)
|
||||||
|
if end >= 0:
|
||||||
|
end += len('</table>') + 1
|
||||||
|
html = html[:start] + html[end:]
|
||||||
|
fp = open(filename, 'w')
|
||||||
|
try:
|
||||||
|
fp.write(html)
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
|
||||||
|
_VERSION_SEARCH = _re.compile(
|
||||||
|
r'\bversion\s+(?P<major>\d+)\.(?P<minor>\d+)'
|
||||||
|
).search
|
||||||
|
def epydoc(**kwargs):
|
||||||
|
""" Run epydoc """
|
||||||
|
# pylint: disable = R0912
|
||||||
|
prog = kwargs.get('epydoc') or 'epydoc'
|
||||||
|
if not _os.path.dirname(_os.path.normpath(prog)):
|
||||||
|
prog = _shell.frompath(prog)
|
||||||
|
if not prog:
|
||||||
|
_term.red("%(epydoc)s not found",
|
||||||
|
epydoc=kwargs.get('epydoc') or 'epydoc',
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
version = _VERSION_SEARCH(_shell.spawn(prog, "--version", stdout=True))
|
||||||
|
if version is not None:
|
||||||
|
try:
|
||||||
|
version = tuple(map(int, version.group('major', 'minor')))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
version = None
|
||||||
|
if version is None:
|
||||||
|
_term.red("%(prog)s version not recognized" % locals())
|
||||||
|
return False
|
||||||
|
|
||||||
|
if version < (3, 0):
|
||||||
|
_term.red("%(prog)s is too old %(version)r < (3, 0)" % locals())
|
||||||
|
return False
|
||||||
|
|
||||||
|
env = dict(_os.environ)
|
||||||
|
|
||||||
|
prepend = kwargs.get('prepend')
|
||||||
|
if prepend:
|
||||||
|
toprepend = _os.pathsep.join(map(str, prepend))
|
||||||
|
if 'PYTHONPATH' in env:
|
||||||
|
env['PYTHONPATH'] = _os.pathsep.join((
|
||||||
|
toprepend, env['PYTHONPATH']
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
env['PYTHONPATH'] = toprepend
|
||||||
|
|
||||||
|
append = kwargs.get('append')
|
||||||
|
if append:
|
||||||
|
toappend = _os.pathsep.join(map(str, append))
|
||||||
|
if 'PYTHONPATH' in env:
|
||||||
|
env['PYTHONPATH'] = _os.pathsep.join((
|
||||||
|
env['PYTHONPATH'], toappend
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
env['PYTHONPATH'] = toappend
|
||||||
|
|
||||||
|
moreenv = kwargs.get('env')
|
||||||
|
if moreenv:
|
||||||
|
env.update(moreenv)
|
||||||
|
|
||||||
|
config = kwargs.get('config') or _shell.native('docs/epydoc.conf')
|
||||||
|
|
||||||
|
argv = [prog, '--config', config]
|
||||||
|
res = not _shell.spawn(*argv, **{'env': env})
|
||||||
|
if res:
|
||||||
|
cfg = _util.SafeConfigParser()
|
||||||
|
cfg.read(config)
|
||||||
|
try:
|
||||||
|
target = dict(cfg.items('epydoc'))['target']
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
_cleanup_epydoc(target)
|
||||||
|
return res
|
50
_setup/py2/dev/userdoc.py
Normal file
50
_setup/py2/dev/userdoc.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
===================
|
||||||
|
User doc builders
|
||||||
|
===================
|
||||||
|
|
||||||
|
User doc builders.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import os as _os
|
||||||
|
|
||||||
|
from _setup import shell as _shell
|
||||||
|
from _setup import term as _term
|
||||||
|
|
||||||
|
|
||||||
|
def sphinx(**kwargs):
|
||||||
|
""" Run sphinx """
|
||||||
|
prog = _shell.frompath('sphinx-build')
|
||||||
|
if prog is None:
|
||||||
|
_term.red("sphinx-build not found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
env = dict(_os.environ)
|
||||||
|
|
||||||
|
argv = [
|
||||||
|
prog, '-a',
|
||||||
|
'-d', _os.path.join(kwargs['build'], 'doctrees'),
|
||||||
|
'-b', 'html',
|
||||||
|
kwargs['source'],
|
||||||
|
kwargs['target'],
|
||||||
|
]
|
||||||
|
|
||||||
|
return not _shell.spawn(*argv, **{'env': env})
|
51
_setup/py2/dist.py
Normal file
51
_setup/py2/dist.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
================
|
||||||
|
dist utilities
|
||||||
|
================
|
||||||
|
|
||||||
|
dist utilities.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import shell as _shell
|
||||||
|
|
||||||
|
|
||||||
|
def run_setup(*args, **kwargs):
|
||||||
|
""" Run setup """
|
||||||
|
if 'setup' in kwargs:
|
||||||
|
script = kwargs.get('setup') or 'setup.py'
|
||||||
|
del kwargs['setup']
|
||||||
|
else:
|
||||||
|
script = 'setup.py'
|
||||||
|
if 'fakeroot' in kwargs:
|
||||||
|
fakeroot = kwargs['fakeroot']
|
||||||
|
del kwargs['fakeroot']
|
||||||
|
else:
|
||||||
|
fakeroot = None
|
||||||
|
if kwargs:
|
||||||
|
raise TypeError("Unrecognized keyword parameters")
|
||||||
|
|
||||||
|
script = _shell.native(script)
|
||||||
|
argv = [_sys.executable, script] + list(args)
|
||||||
|
if fakeroot:
|
||||||
|
argv.insert(0, fakeroot)
|
||||||
|
return not _shell.spawn(*argv)
|
254
_setup/py2/ext.py
Normal file
254
_setup/py2/ext.py
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
===================
|
||||||
|
C extension tools
|
||||||
|
===================
|
||||||
|
|
||||||
|
C extension tools.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
__test__ = False
|
||||||
|
|
||||||
|
from distutils import core as _core
|
||||||
|
from distutils import errors as _distutils_errors
|
||||||
|
import os as _os
|
||||||
|
import posixpath as _posixpath
|
||||||
|
import shutil as _shutil
|
||||||
|
import tempfile as _tempfile
|
||||||
|
|
||||||
|
from _setup import commands as _commands
|
||||||
|
from _setup.util import log
|
||||||
|
|
||||||
|
|
||||||
|
def _install_finalizer(installer):
|
||||||
|
if installer.without_c_extensions:
|
||||||
|
installer.distribution.ext_modules = []
|
||||||
|
|
||||||
|
def _build_finalizer(builder):
|
||||||
|
if builder.without_c_extensions:
|
||||||
|
builder.extensions = []
|
||||||
|
|
||||||
|
|
||||||
|
class Extension(_core.Extension):
|
||||||
|
"""
|
||||||
|
Extension with prerequisite check interface
|
||||||
|
|
||||||
|
If your check is cacheable (during the setup run), override
|
||||||
|
`cached_check_prerequisites`, `check_prerequisites` otherwise.
|
||||||
|
|
||||||
|
:IVariables:
|
||||||
|
`cached_check` : ``bool``
|
||||||
|
The cached check result
|
||||||
|
"""
|
||||||
|
cached_check = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
""" Initialization """
|
||||||
|
if kwargs.has_key('depends'):
|
||||||
|
self.depends = kwargs['depends'] or []
|
||||||
|
else:
|
||||||
|
self.depends = []
|
||||||
|
_core.Extension.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
# add include path
|
||||||
|
included = _posixpath.join('_setup', 'include')
|
||||||
|
if included not in self.include_dirs:
|
||||||
|
self.include_dirs.append(included)
|
||||||
|
|
||||||
|
# add cext.h to the dependencies
|
||||||
|
cext_h = _posixpath.join(included, 'cext.h')
|
||||||
|
if cext_h not in self.depends:
|
||||||
|
self.depends.append(cext_h)
|
||||||
|
|
||||||
|
_commands.add_option('install_lib', 'without-c-extensions',
|
||||||
|
help_text='Don\'t install C extensions',
|
||||||
|
inherit='install',
|
||||||
|
)
|
||||||
|
_commands.add_finalizer('install_lib', 'c-extensions',
|
||||||
|
_install_finalizer
|
||||||
|
)
|
||||||
|
_commands.add_option('build_ext', 'without-c-extensions',
|
||||||
|
help_text='Don\'t build C extensions',
|
||||||
|
inherit=('build', 'install_lib'),
|
||||||
|
)
|
||||||
|
_commands.add_finalizer('build_ext', 'c-extensions', _build_finalizer)
|
||||||
|
|
||||||
|
def check_prerequisites(self, build):
|
||||||
|
"""
|
||||||
|
Check prerequisites
|
||||||
|
|
||||||
|
The check should cover all dependencies needed for the extension to
|
||||||
|
be built and run. The method can do the following:
|
||||||
|
|
||||||
|
- return a false value: the extension will be built
|
||||||
|
- return a true value: the extension will be skipped. This is useful
|
||||||
|
for optional extensions
|
||||||
|
- raise an exception. This is useful for mandatory extensions
|
||||||
|
|
||||||
|
If the check result is cacheable (during the setup run), override
|
||||||
|
`cached_check_prerequisites` instead.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`build` : `BuildExt`
|
||||||
|
The extension builder
|
||||||
|
|
||||||
|
:Return: Skip the extension?
|
||||||
|
:Rtype: ``bool``
|
||||||
|
"""
|
||||||
|
if self.cached_check is None:
|
||||||
|
log.debug("PREREQ check for %s" % self.name)
|
||||||
|
self.cached_check = self.cached_check_prerequisites(build)
|
||||||
|
else:
|
||||||
|
log.debug("PREREQ check for %s (cached)" % self.name)
|
||||||
|
return self.cached_check
|
||||||
|
|
||||||
|
def cached_check_prerequisites(self, build):
|
||||||
|
"""
|
||||||
|
Check prerequisites
|
||||||
|
|
||||||
|
The check should cover all dependencies needed for the extension to
|
||||||
|
be built and run. The method can do the following:
|
||||||
|
|
||||||
|
- return a false value: the extension will be built
|
||||||
|
- return a true value: the extension will be skipped. This is useful
|
||||||
|
for optional extensions
|
||||||
|
- raise an exception. This is useful for mandatory extensions
|
||||||
|
|
||||||
|
If the check result is *not* cacheable (during the setup run),
|
||||||
|
override `check_prerequisites` instead.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`build` : `BuildExt`
|
||||||
|
The extension builder
|
||||||
|
|
||||||
|
:Return: Skip the extension?
|
||||||
|
:Rtype: ``bool``
|
||||||
|
"""
|
||||||
|
# pylint: disable = W0613
|
||||||
|
log.debug("Nothing to check for %s!" % self.name)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class ConfTest(object):
|
||||||
|
"""
|
||||||
|
Single conftest abstraction
|
||||||
|
|
||||||
|
:IVariables:
|
||||||
|
`_tempdir` : ``str``
|
||||||
|
The tempdir created for this test
|
||||||
|
|
||||||
|
`src` : ``str``
|
||||||
|
Name of the source file
|
||||||
|
|
||||||
|
`target` : ``str``
|
||||||
|
Target filename
|
||||||
|
|
||||||
|
`compiler` : ``CCompiler``
|
||||||
|
compiler instance
|
||||||
|
|
||||||
|
`obj` : ``list``
|
||||||
|
List of object filenames (``[str, ...]``)
|
||||||
|
"""
|
||||||
|
_tempdir = None
|
||||||
|
|
||||||
|
def __init__(self, build, source):
|
||||||
|
"""
|
||||||
|
Initialization
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`build` : ``distuils.command.build_ext.build_ext``
|
||||||
|
builder instance
|
||||||
|
|
||||||
|
`source` : ``str``
|
||||||
|
Source of the file to compile
|
||||||
|
"""
|
||||||
|
self._tempdir = tempdir = _tempfile.mkdtemp()
|
||||||
|
src = _os.path.join(tempdir, 'conftest.c')
|
||||||
|
fp = open(src, 'w')
|
||||||
|
try:
|
||||||
|
fp.write(source)
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
self.src = src
|
||||||
|
self.compiler = compiler = build.compiler
|
||||||
|
self.target = _os.path.join(tempdir, 'conftest')
|
||||||
|
self.obj = compiler.object_filenames([src], output_dir=tempdir)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
""" Destruction """
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
""" Destroy the conftest leftovers on disk """
|
||||||
|
tempdir, self._tempdir = self._tempdir, None
|
||||||
|
if tempdir is not None:
|
||||||
|
_shutil.rmtree(tempdir)
|
||||||
|
|
||||||
|
def compile(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Compile the conftest
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`kwargs` : ``dict``
|
||||||
|
Optional keyword parameters for the compiler call
|
||||||
|
|
||||||
|
:Return: Was the compilation successful?
|
||||||
|
:Rtype: ``bool``
|
||||||
|
"""
|
||||||
|
kwargs['output_dir'] = self._tempdir
|
||||||
|
try:
|
||||||
|
self.compiler.compile([self.src], **kwargs)
|
||||||
|
except _distutils_errors.CompileError:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def link(self, **kwargs):
|
||||||
|
r"""
|
||||||
|
Link the conftest
|
||||||
|
|
||||||
|
Before you can link the conftest objects they need to be `compile`\d.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`kwargs` : ``dict``
|
||||||
|
Optional keyword parameters for the linker call
|
||||||
|
|
||||||
|
:Return: Was the linking successful?
|
||||||
|
:Rtype: ``bool``
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.compiler.link_executable(self.obj, self.target, **kwargs)
|
||||||
|
except _distutils_errors.LinkError:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def pipe(self, mode="r"):
|
||||||
|
r"""
|
||||||
|
Execute the conftest binary and connect to it using a pipe
|
||||||
|
|
||||||
|
Before you can pipe to or from the conftest binary it needs to
|
||||||
|
be `link`\ed.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`mode` : ``str``
|
||||||
|
Pipe mode - r/w
|
||||||
|
|
||||||
|
:Return: The open pipe
|
||||||
|
:Rtype: ``file``
|
||||||
|
"""
|
||||||
|
return _os.popen(self.compiler.executable_filename(self.target), mode)
|
28
_setup/py2/make/__init__.py
Normal file
28
_setup/py2/make/__init__.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=====================
|
||||||
|
Package _setup.make
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Make tools, not distributed.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
# pylint: disable = W0611
|
||||||
|
from _setup.make._make import main, fail, warn, fatal, Target
|
338
_setup/py2/make/_make.py
Normal file
338
_setup/py2/make/_make.py
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
==================
|
||||||
|
Simple make base
|
||||||
|
==================
|
||||||
|
|
||||||
|
Simple make base.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import term as _term
|
||||||
|
|
||||||
|
|
||||||
|
class Failure(SystemExit):
|
||||||
|
""" Failure exception """
|
||||||
|
|
||||||
|
|
||||||
|
def fail(reason):
|
||||||
|
""" Fail for a reason """
|
||||||
|
raise Failure(reason)
|
||||||
|
|
||||||
|
|
||||||
|
def warn(message, name=None):
|
||||||
|
""" Warn """
|
||||||
|
_term.red("%(NAME)sWarning: %(msg)s",
|
||||||
|
NAME=name and "%s:" % name or '', msg=message
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def fatal(reason):
|
||||||
|
""" Fatal error, immediate stop """
|
||||||
|
print >> _sys.stderr, reason
|
||||||
|
_sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
class Target(object):
|
||||||
|
""" Target base class """
|
||||||
|
NAME = None
|
||||||
|
DEPS = None
|
||||||
|
HIDDEN = False
|
||||||
|
|
||||||
|
ERROR = None
|
||||||
|
|
||||||
|
def __init__(self, runner):
|
||||||
|
""" Base __init__ """
|
||||||
|
self.runner = runner
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
""" Default init hook """
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
""" Default run hook """
|
||||||
|
pass
|
||||||
|
|
||||||
|
def clean(self, scm=True, dist=False):
|
||||||
|
""" Default clean hook """
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class _Runner(object):
|
||||||
|
""" Runner """
|
||||||
|
|
||||||
|
def __init__(self, *targetscollection):
|
||||||
|
""" Initialization """
|
||||||
|
tdict = {}
|
||||||
|
if not targetscollection:
|
||||||
|
import __main__
|
||||||
|
targetscollection = [__main__]
|
||||||
|
|
||||||
|
from _setup.make import default_targets
|
||||||
|
if default_targets not in targetscollection:
|
||||||
|
targetscollection.append(default_targets)
|
||||||
|
|
||||||
|
for targets in targetscollection:
|
||||||
|
for value in vars(targets).values():
|
||||||
|
if isinstance(value, type) and issubclass(value, Target) and \
|
||||||
|
value.NAME is not None:
|
||||||
|
if value.NAME in tdict:
|
||||||
|
if issubclass(value, tdict[value.NAME]):
|
||||||
|
pass # override base target
|
||||||
|
elif issubclass(tdict[value.NAME], value):
|
||||||
|
continue # found base later. ignore
|
||||||
|
else:
|
||||||
|
warn('Ambiguous target name', value.NAME)
|
||||||
|
continue
|
||||||
|
tdict[value.NAME] = value
|
||||||
|
self._tdict = tdict
|
||||||
|
self._itdict = {}
|
||||||
|
|
||||||
|
def print_help(self):
|
||||||
|
""" Print make help """
|
||||||
|
import textwrap as _textwrap
|
||||||
|
|
||||||
|
targets = self.targetinfo()
|
||||||
|
keys = []
|
||||||
|
for key, info in targets.items():
|
||||||
|
if not info['hide']:
|
||||||
|
keys.append(key)
|
||||||
|
keys.sort()
|
||||||
|
length = max(map(len, keys))
|
||||||
|
info = []
|
||||||
|
for key in keys:
|
||||||
|
info.append("%s%s" % (
|
||||||
|
(key + " " * length)[:length + 2],
|
||||||
|
_textwrap.fill(
|
||||||
|
targets[key]['desc'].strip(),
|
||||||
|
subsequent_indent=" " * (length + 2)
|
||||||
|
),
|
||||||
|
))
|
||||||
|
print "Available targets:\n\n" + "\n".join(info)
|
||||||
|
|
||||||
|
def targetinfo(self):
|
||||||
|
""" Extract target information """
|
||||||
|
result = {}
|
||||||
|
for name, cls in self._tdict.items():
|
||||||
|
result[name] = {
|
||||||
|
'desc': cls.__doc__ or "no description",
|
||||||
|
'hide': cls.HIDDEN,
|
||||||
|
'deps': cls.DEPS or (),
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _topleveltargets(self):
|
||||||
|
""" Find all top level targets """
|
||||||
|
rev = {} # key is a dep of [values]
|
||||||
|
all_ = self.targetinfo()
|
||||||
|
for target, info in all_.items():
|
||||||
|
for dep in info['deps']:
|
||||||
|
if dep not in all_:
|
||||||
|
fatal("Unknown target '%s' (dep of %s) -> exit" % (
|
||||||
|
dep, target
|
||||||
|
))
|
||||||
|
rev.setdefault(dep, []).append(target)
|
||||||
|
return [target for target, info in rev.items() if not info]
|
||||||
|
|
||||||
|
def _run(self, target, seen=None):
|
||||||
|
""" Run a target """
|
||||||
|
if target.DEPS:
|
||||||
|
self(*target.DEPS, **{'seen': seen})
|
||||||
|
|
||||||
|
if not target.HIDDEN:
|
||||||
|
_term.yellow(">>> %(name)s", name=target.NAME)
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = target.run()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
result, target.ERROR = False, "^C -> exit"
|
||||||
|
except Failure, e:
|
||||||
|
result, target.ERROR = False, "%s: %s" % (target.NAME, e)
|
||||||
|
except (SystemExit, MemoryError):
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
target.ERROR = "%s errored:\n%s" % (target.NAME, ''.join(
|
||||||
|
traceback.format_exception(*_sys.exc_info())
|
||||||
|
))
|
||||||
|
result = False
|
||||||
|
else:
|
||||||
|
if result is None:
|
||||||
|
result = True
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _clean(self, target, scm, dist, seen=None):
|
||||||
|
""" Run a target """
|
||||||
|
if target.DEPS:
|
||||||
|
self.run_clean(
|
||||||
|
*target.DEPS, **{'scm': scm, 'dist': dist, 'seen': seen}
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = target.clean(scm, dist)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
result, target.ERROR = False, "^C -> exit"
|
||||||
|
except Failure, e:
|
||||||
|
result, target.ERROR = False, "%s: %s" % (target.NAME, e)
|
||||||
|
except (SystemExit, MemoryError):
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
target.ERROR = "%s errored:\n%s" % (target.NAME, ''.join(
|
||||||
|
traceback.format_exception(*_sys.exc_info())
|
||||||
|
))
|
||||||
|
result = False
|
||||||
|
else:
|
||||||
|
if result is None:
|
||||||
|
result = True
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _make_init(self, seen):
|
||||||
|
""" Make init mapper """
|
||||||
|
def init(target):
|
||||||
|
""" Return initialized target """
|
||||||
|
if target not in seen:
|
||||||
|
try:
|
||||||
|
seen[target] = self._tdict[target](self)
|
||||||
|
except KeyError:
|
||||||
|
fatal("Unknown target '%s' -> exit" % target)
|
||||||
|
else:
|
||||||
|
seen[target] = None
|
||||||
|
return seen[target]
|
||||||
|
return init
|
||||||
|
|
||||||
|
def run_clean(self, *targets, **kwargs):
|
||||||
|
""" Run targets """
|
||||||
|
def pop(name, default=None):
|
||||||
|
""" Pop """
|
||||||
|
if name in kwargs:
|
||||||
|
value = kwargs[name]
|
||||||
|
del kwargs[name]
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
seen = pop('seen', {})
|
||||||
|
scm = pop('scm', True)
|
||||||
|
dist = pop('dist', False)
|
||||||
|
if kwargs:
|
||||||
|
raise TypeError('Unknown keyword parameters')
|
||||||
|
|
||||||
|
if not targets:
|
||||||
|
top_targets = self._topleveltargets()
|
||||||
|
targets = self.targetinfo()
|
||||||
|
for item in top_targets:
|
||||||
|
del targets[item]
|
||||||
|
targets = targets.keys()
|
||||||
|
targets.sort()
|
||||||
|
top_targets.sort()
|
||||||
|
targets = top_targets + targets
|
||||||
|
|
||||||
|
init = self._make_init(seen)
|
||||||
|
for name in targets:
|
||||||
|
target = init(name)
|
||||||
|
if target is not None:
|
||||||
|
if not self._clean(target, scm=scm, dist=dist, seen=seen):
|
||||||
|
msg = target.ERROR
|
||||||
|
if msg is None:
|
||||||
|
msg = "Clean target %s returned error -> exit" % name
|
||||||
|
fatal(msg)
|
||||||
|
|
||||||
|
def __call__(self, *targets, **kwargs):
|
||||||
|
""" Run targets """
|
||||||
|
if 'seen' in kwargs:
|
||||||
|
seen = kwargs['seen']
|
||||||
|
del kwargs['seen']
|
||||||
|
else:
|
||||||
|
seen = None
|
||||||
|
if seen is None:
|
||||||
|
seen = self._itdict
|
||||||
|
if kwargs:
|
||||||
|
raise TypeError('Unknown keyword parameters')
|
||||||
|
|
||||||
|
init = self._make_init(seen)
|
||||||
|
for name in targets:
|
||||||
|
target = init(name)
|
||||||
|
if target is not None:
|
||||||
|
if not self._run(target, seen):
|
||||||
|
msg = target.ERROR
|
||||||
|
if msg is None:
|
||||||
|
msg = "Target %s returned error -> exit" % name
|
||||||
|
fatal(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def main(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
main(argv=None, *args, name=None)
|
||||||
|
|
||||||
|
Main start point. This function parses the command line and executes the
|
||||||
|
targets given through `argv`. If there are no targets given, a help output
|
||||||
|
is generated.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`argv` : sequence
|
||||||
|
Command line arguments. If omitted or ``None``, they are picked from
|
||||||
|
``sys.argv``.
|
||||||
|
|
||||||
|
`args` : ``tuple``
|
||||||
|
The list of modules with targets. If omitted, ``__main__``
|
||||||
|
is imported and treated as target module. Additionally the mechanism
|
||||||
|
always adds the `_setup.make` module (this one) to the list in order
|
||||||
|
to grab some default targets.
|
||||||
|
|
||||||
|
`name` : ``str``
|
||||||
|
Name of the executing module. If omitted or ``None``, ``'__main__'``
|
||||||
|
is assumed. If the final name is not ``'__main__'``, the function
|
||||||
|
returns immediately.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
name = kwargs['name']
|
||||||
|
except KeyError:
|
||||||
|
name = '__main__'
|
||||||
|
else:
|
||||||
|
del kwargs['name']
|
||||||
|
if name is None:
|
||||||
|
name = '__main__'
|
||||||
|
|
||||||
|
try:
|
||||||
|
argv = kwargs['argv']
|
||||||
|
except KeyError:
|
||||||
|
if not args:
|
||||||
|
args = (None,)
|
||||||
|
else:
|
||||||
|
del kwargs['argv']
|
||||||
|
args = (argv,) + args
|
||||||
|
|
||||||
|
if kwargs:
|
||||||
|
raise TypeError("Unrecognized keyword arguments for main()")
|
||||||
|
|
||||||
|
if name == '__main__':
|
||||||
|
argv, args = args[0], args[1:]
|
||||||
|
if argv is None:
|
||||||
|
argv = _sys.argv[1:]
|
||||||
|
|
||||||
|
runner = _Runner(*args)
|
||||||
|
if argv:
|
||||||
|
runner(*argv)
|
||||||
|
else:
|
||||||
|
runner.print_help()
|
110
_setup/py2/make/default_targets.py
Normal file
110
_setup/py2/make/default_targets.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
==================
|
||||||
|
Simple make base
|
||||||
|
==================
|
||||||
|
|
||||||
|
Simple make base.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import os as _os
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import make as _make
|
||||||
|
from _setup import shell as _shell
|
||||||
|
|
||||||
|
|
||||||
|
class MakefileTarget(_make.Target):
|
||||||
|
""" Create a make file """
|
||||||
|
NAME = 'makefile'
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
def escape(value):
|
||||||
|
""" Escape for make and shell """
|
||||||
|
return '"%s"' % value.replace(
|
||||||
|
'\\', '\\\\').replace(
|
||||||
|
'"', '\\"').replace(
|
||||||
|
'$', '\\$$')
|
||||||
|
def decorate(line, prefix='# ', width=78, char='~', padding=' '):
|
||||||
|
""" Decorate a line """
|
||||||
|
line = line.center(width - len(prefix))
|
||||||
|
return '%s%s%s%s%s%s' % (
|
||||||
|
prefix,
|
||||||
|
char * (len(line) - len(line.lstrip()) - len(padding)),
|
||||||
|
padding,
|
||||||
|
line.strip(),
|
||||||
|
padding,
|
||||||
|
char * (len(line) - len(line.rstrip()) - len(padding)),
|
||||||
|
)
|
||||||
|
|
||||||
|
python = escape(_sys.executable)
|
||||||
|
script = escape(_sys.argv[0])
|
||||||
|
targets = self.runner.targetinfo()
|
||||||
|
names = []
|
||||||
|
for name, info in targets.items():
|
||||||
|
if not info['hide']:
|
||||||
|
names.append(name)
|
||||||
|
names.sort()
|
||||||
|
|
||||||
|
fp = open(_shell.native('Makefile'), 'w')
|
||||||
|
print >> fp, decorate("Generated Makefile, DO NOT EDIT")
|
||||||
|
print >> fp, decorate("python %s %s" % (
|
||||||
|
_os.path.basename(script), self.NAME
|
||||||
|
))
|
||||||
|
print >> fp
|
||||||
|
print >> fp, "_default_:"
|
||||||
|
print >> fp, "\t@%s %s" % (python, script)
|
||||||
|
for name in names:
|
||||||
|
print >> fp, "\n"
|
||||||
|
print >> fp, "# %s" % \
|
||||||
|
targets[name]['desc'].splitlines()[0].strip()
|
||||||
|
print >> fp, "%s:" % name
|
||||||
|
print >> fp, "\t@%s %s %s" % (python, script, escape(name))
|
||||||
|
print >> fp
|
||||||
|
extension = self.extend(names)
|
||||||
|
if extension is not None:
|
||||||
|
print >> fp, extension
|
||||||
|
print >> fp
|
||||||
|
print >> fp, ".PHONY: _default_ %s\n\n" % ' '.join(names)
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
def extend(self, names):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CleanTarget(_make.Target):
|
||||||
|
""" Clean the mess """
|
||||||
|
NAME = 'clean'
|
||||||
|
_scm, _dist = True, False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.runner.run_clean(scm=self._scm, dist=self._dist)
|
||||||
|
|
||||||
|
|
||||||
|
class DistCleanTarget(CleanTarget):
|
||||||
|
""" Clean as freshly unpacked dist package """
|
||||||
|
NAME = 'distclean'
|
||||||
|
_scm, _dist = False, True
|
||||||
|
|
||||||
|
|
||||||
|
class ExtraCleanTarget(CleanTarget):
|
||||||
|
""" Clean everything """
|
||||||
|
NAME = 'extraclean'
|
||||||
|
_scm, _dist = True, True
|
324
_setup/py2/make/targets.py
Normal file
324
_setup/py2/make/targets.py
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007 - 2013
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
==================
|
||||||
|
Standard targets
|
||||||
|
==================
|
||||||
|
|
||||||
|
Standard targets.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import os as _os
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import dist as _dist
|
||||||
|
from _setup import make as _make
|
||||||
|
from _setup import shell as _shell
|
||||||
|
from _setup import term as _term
|
||||||
|
|
||||||
|
|
||||||
|
class Distribution(_make.Target):
|
||||||
|
""" Build a distribution """
|
||||||
|
NAME = "dist"
|
||||||
|
DEPS = ["MANIFEST"]
|
||||||
|
|
||||||
|
_dist, _ebuilds, _changes = None, None, None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
exts = self.dist_pkg()
|
||||||
|
digests = self.digest_files(exts)
|
||||||
|
self.sign_digests(digests)
|
||||||
|
self.copy_ebuilds()
|
||||||
|
self.copy_changes()
|
||||||
|
|
||||||
|
def dist_pkg(self):
|
||||||
|
_term.green("Building package...")
|
||||||
|
_dist.run_setup("sdist", "--formats", "tar,zip",
|
||||||
|
fakeroot=_shell.frompath('fakeroot')
|
||||||
|
)
|
||||||
|
exts = ['.zip']
|
||||||
|
for name in _shell.files(self._dist, '*.tar', False):
|
||||||
|
exts.extend(self.compress(name))
|
||||||
|
_shell.rm(name)
|
||||||
|
return exts
|
||||||
|
|
||||||
|
def compress(self, filename):
|
||||||
|
""" Compress file """
|
||||||
|
ext = _os.path.splitext(filename)[1]
|
||||||
|
exts = []
|
||||||
|
exts.append('.'.join((ext, self.compress_gzip(filename))))
|
||||||
|
exts.append('.'.join((ext, self.compress_bzip2(filename))))
|
||||||
|
exts.append('.'.join((ext, self.compress_xz(filename))))
|
||||||
|
return exts
|
||||||
|
|
||||||
|
def compress_xz(self, filename):
|
||||||
|
outfilename = filename + '.xz'
|
||||||
|
self.compress_external(filename, outfilename, 'xz', '-c9')
|
||||||
|
return 'xz'
|
||||||
|
|
||||||
|
def compress_bzip2(self, filename):
|
||||||
|
outfilename = filename + '.bz2'
|
||||||
|
try:
|
||||||
|
import bz2 as _bz2
|
||||||
|
except ImportError:
|
||||||
|
self.compress_external(filename, outfilename, 'bzip2', '-c9')
|
||||||
|
else:
|
||||||
|
outfile = _bz2.BZ2File(outfilename, 'w')
|
||||||
|
self.compress_internal(filename, outfile, outfilename)
|
||||||
|
return 'bz2'
|
||||||
|
|
||||||
|
def compress_gzip(self, filename):
|
||||||
|
outfilename = filename + '.gz'
|
||||||
|
try:
|
||||||
|
import gzip as _gzip
|
||||||
|
except ImportError:
|
||||||
|
self.compress_external(filename, outfilename, 'gzip', '-c9')
|
||||||
|
else:
|
||||||
|
outfile = _gzip.GzipFile(filename, 'wb',
|
||||||
|
fileobj=open(outfilename, 'wb')
|
||||||
|
)
|
||||||
|
self.compress_internal(filename, outfile, outfilename)
|
||||||
|
return 'gz'
|
||||||
|
|
||||||
|
def compress_external(self, infile, outfile, *argv):
|
||||||
|
argv = list(argv)
|
||||||
|
argv[0] = _shell.frompath(argv[0])
|
||||||
|
if argv[0] is not None:
|
||||||
|
return not _shell.spawn(*argv, **{
|
||||||
|
'filepipe': True, 'stdin': infile, 'stdout': outfile,
|
||||||
|
})
|
||||||
|
return None
|
||||||
|
|
||||||
|
def compress_internal(self, filename, outfile, outfilename):
|
||||||
|
infile = open(filename, 'rb')
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
while 1:
|
||||||
|
chunk = infile.read(8192)
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
outfile.write(chunk)
|
||||||
|
outfile.close()
|
||||||
|
except:
|
||||||
|
e = _sys.exc_info()
|
||||||
|
try:
|
||||||
|
_shell.rm(outfilename)
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
raise e[0], e[1], e[2]
|
||||||
|
finally:
|
||||||
|
del e
|
||||||
|
finally:
|
||||||
|
infile.close()
|
||||||
|
|
||||||
|
def digest_files(self, exts):
|
||||||
|
""" digest files """
|
||||||
|
digests = {}
|
||||||
|
digestnames = {}
|
||||||
|
for ext in exts:
|
||||||
|
for name in _shell.files(self._dist, '*' + ext, False):
|
||||||
|
basename = _os.path.basename(name)
|
||||||
|
if basename not in digests:
|
||||||
|
digests[basename] = []
|
||||||
|
digests[basename].extend(self.digest(name))
|
||||||
|
digestname = basename[:-len(ext)]
|
||||||
|
if digestname not in digestnames:
|
||||||
|
digestnames[digestname] = []
|
||||||
|
digestnames[digestname].append(basename)
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for name, basenames in digestnames.items():
|
||||||
|
result.append(_os.path.join(self._dist, name + '.digests'))
|
||||||
|
fp = open(result[-1], 'wb')
|
||||||
|
try:
|
||||||
|
fp.write(
|
||||||
|
'\n# The file may contain MD5, SHA1 and SHA256 digests\n'
|
||||||
|
)
|
||||||
|
fp.write('# Check archive integrity with, e.g. md5sum -c\n')
|
||||||
|
fp.write('# Check digest file integrity with PGP\n\n')
|
||||||
|
basenames.sort()
|
||||||
|
for basename in basenames:
|
||||||
|
for digest in digests[basename]:
|
||||||
|
fp.write("%s *%s\n" % (digest, basename))
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def digest(self, filename):
|
||||||
|
result = []
|
||||||
|
for method in (self.md5, self.sha1, self.sha256):
|
||||||
|
digest = method(filename)
|
||||||
|
if digest is not None:
|
||||||
|
result.append(digest)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def do_digest(self, hashfunc, name, filename):
|
||||||
|
filename = _shell.native(filename)
|
||||||
|
_term.green("%(digest)s-digesting %(name)s...",
|
||||||
|
digest=name, name=_os.path.basename(filename))
|
||||||
|
fp = open(filename, 'rb')
|
||||||
|
sig = hashfunc()
|
||||||
|
block = fp.read(8192)
|
||||||
|
while block:
|
||||||
|
sig.update(block)
|
||||||
|
block = fp.read(8192)
|
||||||
|
fp.close()
|
||||||
|
return sig.hexdigest()
|
||||||
|
|
||||||
|
param = {'sig': sig.hexdigest(), 'file': _os.path.basename(filename)}
|
||||||
|
fp = open("%s.%s" % (filename, name), "w")
|
||||||
|
fp.write("%(sig)s *%(file)s\n" % param)
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def md5(self, filename):
|
||||||
|
try:
|
||||||
|
from hashlib import md5
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
from md5 import new as md5
|
||||||
|
except ImportError:
|
||||||
|
_make.warn("md5 not found -> skip md5 digests", self.NAME)
|
||||||
|
return None
|
||||||
|
return self.do_digest(md5, "md5", filename)
|
||||||
|
|
||||||
|
def sha1(self, filename):
|
||||||
|
try:
|
||||||
|
from hashlib import sha1
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
from sha import new as sha1
|
||||||
|
except ImportError:
|
||||||
|
_make.warn("sha1 not found -> skip sha1 digests", self.NAME)
|
||||||
|
return None
|
||||||
|
return self.do_digest(sha1, "sha1", filename)
|
||||||
|
|
||||||
|
def sha256(self, filename):
|
||||||
|
try:
|
||||||
|
from hashlib import sha256
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
from Crypto.Hash.SHA256 import new as sha256
|
||||||
|
except ImportError:
|
||||||
|
_make.warn(
|
||||||
|
"sha256 not found -> skip sha256 digests", self.NAME
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
return self.do_digest(sha256, "sha256", filename)
|
||||||
|
|
||||||
|
def copy_ebuilds(self):
|
||||||
|
if self._ebuilds is not None:
|
||||||
|
for src in _shell.files(self._ebuilds, '*.ebuild'):
|
||||||
|
_shell.cp(src, self._dist)
|
||||||
|
|
||||||
|
def copy_changes(self):
|
||||||
|
if self._changes is not None:
|
||||||
|
_shell.cp(self._changes, self._dist)
|
||||||
|
|
||||||
|
def sign_digests(self, digests):
|
||||||
|
for digest in digests:
|
||||||
|
self.sign(digest, detach=False)
|
||||||
|
|
||||||
|
def sign(self, filename, detach=True):
|
||||||
|
filename = _shell.native(filename)
|
||||||
|
try:
|
||||||
|
from pyme import core, errors
|
||||||
|
from pyme.constants.sig import mode
|
||||||
|
except ImportError:
|
||||||
|
return self.sign_external(filename, detach=detach)
|
||||||
|
|
||||||
|
_term.green("signing %(name)s...", name=_os.path.basename(filename))
|
||||||
|
sigmode = [mode.CLEAR, mode.DETACH][bool(detach)]
|
||||||
|
fp = core.Data(file=filename)
|
||||||
|
sig = core.Data()
|
||||||
|
try:
|
||||||
|
c = core.Context()
|
||||||
|
except errors.GPGMEError:
|
||||||
|
return self.sign_external(filename, detach=detach)
|
||||||
|
c.set_armor(1)
|
||||||
|
try:
|
||||||
|
c.op_sign(fp, sig, sigmode)
|
||||||
|
except errors.GPGMEError, e:
|
||||||
|
_make.fail(str(e))
|
||||||
|
|
||||||
|
sig.seek(0, 0)
|
||||||
|
if detach:
|
||||||
|
open("%s.asc" % filename, "w").write(sig.read())
|
||||||
|
else:
|
||||||
|
open(filename, "w").write(sig.read())
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def sign_external(self, filename, detach=True):
|
||||||
|
""" Sign calling gpg """
|
||||||
|
gpg = _shell.frompath('gpg')
|
||||||
|
if gpg is None:
|
||||||
|
_make.warn('GPG not found -> cannot sign')
|
||||||
|
return False
|
||||||
|
if detach:
|
||||||
|
_shell.spawn(gpg,
|
||||||
|
'--armor',
|
||||||
|
'--output', filename + '.asc',
|
||||||
|
'--detach-sign',
|
||||||
|
'--',
|
||||||
|
filename,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_shell.spawn(gpg,
|
||||||
|
'--output', filename + '.signed',
|
||||||
|
'--clearsign',
|
||||||
|
'--',
|
||||||
|
filename,
|
||||||
|
)
|
||||||
|
_os.rename(filename + '.signed', filename)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def clean(self, scm, dist):
|
||||||
|
_term.green("Removing dist files...")
|
||||||
|
_shell.rm_rf(self._dist)
|
||||||
|
|
||||||
|
|
||||||
|
class Manifest(_make.Target):
|
||||||
|
""" Create manifest """
|
||||||
|
NAME = "MANIFEST"
|
||||||
|
HIDDEN = True
|
||||||
|
DEPS = ["doc"]
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
_term.green("Creating %(name)s...", name=self.NAME)
|
||||||
|
dest = _shell.native(self.NAME)
|
||||||
|
dest = open(dest, 'w')
|
||||||
|
for name in self.manifest_names():
|
||||||
|
dest.write("%s\n" % name)
|
||||||
|
dest.close()
|
||||||
|
|
||||||
|
def manifest_names(self):
|
||||||
|
import setup
|
||||||
|
for item in setup.manifest():
|
||||||
|
yield item
|
||||||
|
|
||||||
|
def clean(self, scm, dist):
|
||||||
|
""" Clean manifest """
|
||||||
|
if scm:
|
||||||
|
_term.green("Removing MANIFEST")
|
||||||
|
_shell.rm(self.NAME)
|
419
_setup/py2/setup.py
Normal file
419
_setup/py2/setup.py
Normal file
@ -0,0 +1,419 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007 - 2013
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
===================
|
||||||
|
Main setup runner
|
||||||
|
===================
|
||||||
|
|
||||||
|
This module provides a wrapper around the distutils core setup.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import ConfigParser as _config_parser
|
||||||
|
from distutils import core as _core
|
||||||
|
import os as _os
|
||||||
|
import posixpath as _posixpath
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import commands as _commands
|
||||||
|
from _setup import data as _data
|
||||||
|
from _setup import ext as _ext
|
||||||
|
from _setup import util as _util
|
||||||
|
from _setup import shell as _shell
|
||||||
|
|
||||||
|
|
||||||
|
def check_python_version(impl, version_min, version_max):
|
||||||
|
""" Check python version """
|
||||||
|
if impl == 'python':
|
||||||
|
version_info = _sys.version_info
|
||||||
|
elif impl == 'pypy':
|
||||||
|
version_info = getattr(_sys, 'pypy_version_info', None)
|
||||||
|
if not version_info:
|
||||||
|
return
|
||||||
|
elif impl == 'jython':
|
||||||
|
if not 'java' in _sys.platform.lower():
|
||||||
|
return
|
||||||
|
version_info = _sys.version_info
|
||||||
|
else:
|
||||||
|
raise AssertionError("impl not in ('python', 'pypy', 'jython')")
|
||||||
|
|
||||||
|
pyversion = map(int, version_info[:3])
|
||||||
|
if version_min:
|
||||||
|
min_required = \
|
||||||
|
map(int, '.'.join((version_min, '0.0.0')).split('.')[:3])
|
||||||
|
if pyversion < min_required:
|
||||||
|
raise EnvironmentError("Need at least %s %s (vs. %s)" % (
|
||||||
|
impl, version_min, '.'.join(map(str, pyversion))
|
||||||
|
))
|
||||||
|
if version_max:
|
||||||
|
max_required = map(int, version_max.split('.'))
|
||||||
|
max_required[-1] += 1
|
||||||
|
if pyversion >= max_required:
|
||||||
|
raise EnvironmentError("Need at max %s %s (vs. %s)" % (
|
||||||
|
impl,
|
||||||
|
version_max,
|
||||||
|
'.'.join(map(str, pyversion))
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def find_description(docs):
|
||||||
|
"""
|
||||||
|
Determine the package description from DESCRIPTION
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`docs` : ``dict``
|
||||||
|
Docs config section
|
||||||
|
|
||||||
|
:Return: Tuple of summary, description and license
|
||||||
|
(``('summary', 'description', 'license')``)
|
||||||
|
(all may be ``None``)
|
||||||
|
:Rtype: ``tuple``
|
||||||
|
"""
|
||||||
|
summary = None
|
||||||
|
filename = docs.get('meta.summary', 'SUMMARY').strip()
|
||||||
|
if filename and _os.path.isfile(filename):
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
summary = fp.read().strip().splitlines()[0].rstrip()
|
||||||
|
except IndexError:
|
||||||
|
summary = ''
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
description = None
|
||||||
|
filename = docs.get('meta.description', 'DESCRIPTION').strip()
|
||||||
|
if filename and _os.path.isfile(filename):
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
description = fp.read().rstrip()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
if summary is None and description:
|
||||||
|
from docutils import core
|
||||||
|
summary = core.publish_parts(
|
||||||
|
source=description,
|
||||||
|
source_path=filename,
|
||||||
|
writer_name='html',
|
||||||
|
)['title'].encode('utf-8')
|
||||||
|
|
||||||
|
return summary, description
|
||||||
|
|
||||||
|
|
||||||
|
def find_classifiers(docs):
|
||||||
|
"""
|
||||||
|
Determine classifiers from CLASSIFIERS
|
||||||
|
|
||||||
|
:return: List of classifiers (``['classifier', ...]``)
|
||||||
|
:rtype: ``list``
|
||||||
|
"""
|
||||||
|
filename = docs.get('meta.classifiers', 'CLASSIFIERS').strip()
|
||||||
|
if filename and _os.path.isfile(filename):
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
content = fp.read()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
content = [item.strip() for item in content.splitlines()]
|
||||||
|
return [item for item in content if item and not item.startswith('#')]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def find_provides(docs):
|
||||||
|
"""
|
||||||
|
Determine provides from PROVIDES
|
||||||
|
|
||||||
|
:return: List of provides (``['provides', ...]``)
|
||||||
|
:rtype: ``list``
|
||||||
|
"""
|
||||||
|
filename = docs.get('meta.provides', 'PROVIDES').strip()
|
||||||
|
if filename and _os.path.isfile(filename):
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
content = fp.read()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
content = [item.strip() for item in content.splitlines()]
|
||||||
|
return [item for item in content if item and not item.startswith('#')]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def find_license(docs):
|
||||||
|
"""
|
||||||
|
Determine license from LICENSE
|
||||||
|
|
||||||
|
:return: License text
|
||||||
|
:rtype: ``str``
|
||||||
|
"""
|
||||||
|
filename = docs.get('meta.license', 'LICENSE').strip()
|
||||||
|
if filename and _os.path.isfile(filename):
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
return fp.read().rstrip()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_packages(manifest):
|
||||||
|
""" Determine packages and subpackages """
|
||||||
|
packages = {}
|
||||||
|
collect = manifest.get('packages.collect', '').split()
|
||||||
|
lib = manifest.get('packages.lib', '.')
|
||||||
|
try:
|
||||||
|
sep = _os.path.sep
|
||||||
|
except AttributeError:
|
||||||
|
sep = _os.path.join('1', '2')[1:-1]
|
||||||
|
for root in collect:
|
||||||
|
for dirpath, _, filenames in _shell.walk(_os.path.join(lib, root)):
|
||||||
|
if dirpath.find('.svn') >= 0 or dirpath.find('.git') >= 0:
|
||||||
|
continue
|
||||||
|
if '__init__.py' in filenames:
|
||||||
|
packages[
|
||||||
|
_os.path.normpath(dirpath).replace(sep, '.')
|
||||||
|
] = None
|
||||||
|
packages = packages.keys()
|
||||||
|
packages.sort()
|
||||||
|
return packages
|
||||||
|
|
||||||
|
|
||||||
|
def find_data(name, docs):
|
||||||
|
""" Determine data files """
|
||||||
|
result = []
|
||||||
|
if docs.get('extra', '').strip():
|
||||||
|
result.append(_data.Documentation(docs['extra'].split(),
|
||||||
|
prefix='share/doc/%s' % name,
|
||||||
|
))
|
||||||
|
if docs.get('examples.dir', '').strip():
|
||||||
|
tpl = ['recursive-include %s *' % docs['examples.dir']]
|
||||||
|
if docs.get('examples.ignore', '').strip():
|
||||||
|
tpl.extend(["global-exclude %s" % item
|
||||||
|
for item in docs['examples.ignore'].split()
|
||||||
|
])
|
||||||
|
strip = int(docs.get('examples.strip', '') or 0)
|
||||||
|
result.append(_data.Documentation.from_templates(*tpl, **{
|
||||||
|
'strip': strip,
|
||||||
|
'prefix': 'share/doc/%s' % name,
|
||||||
|
'preserve': 1,
|
||||||
|
}))
|
||||||
|
if docs.get('userdoc.dir', '').strip():
|
||||||
|
tpl = ['recursive-include %s *' % docs['userdoc.dir']]
|
||||||
|
if docs.get('userdoc.ignore', '').strip():
|
||||||
|
tpl.extend(["global-exclude %s" % item
|
||||||
|
for item in docs['userdoc.ignore'].split()
|
||||||
|
])
|
||||||
|
strip = int(docs.get('userdoc.strip', '') or 0)
|
||||||
|
result.append(_data.Documentation.from_templates(*tpl, **{
|
||||||
|
'strip': strip,
|
||||||
|
'prefix': 'share/doc/%s' % name,
|
||||||
|
'preserve': 1,
|
||||||
|
}))
|
||||||
|
if docs.get('apidoc.dir', '').strip():
|
||||||
|
tpl = ['recursive-include %s *' % docs['apidoc.dir']]
|
||||||
|
if docs.get('apidoc.ignore', '').strip():
|
||||||
|
tpl.extend(["global-exclude %s" % item
|
||||||
|
for item in docs['apidoc.ignore'].split()
|
||||||
|
])
|
||||||
|
strip = int(docs.get('apidoc.strip', '') or 0)
|
||||||
|
result.append(_data.Documentation.from_templates(*tpl, **{
|
||||||
|
'strip': strip,
|
||||||
|
'prefix': 'share/doc/%s' % name,
|
||||||
|
'preserve': 1,
|
||||||
|
}))
|
||||||
|
if docs.get('man', '').strip():
|
||||||
|
result.extend(_data.Manpages.dispatch(docs['man'].split()))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def make_manifest(manifest, config, docs, kwargs):
|
||||||
|
""" Create file list to pack up """
|
||||||
|
# pylint: disable = R0912
|
||||||
|
kwargs = kwargs.copy()
|
||||||
|
kwargs['script_args'] = ['install']
|
||||||
|
kwargs['packages'] = list(kwargs.get('packages') or ()) + [
|
||||||
|
'_setup', '_setup.py2', '_setup.py3',
|
||||||
|
] + list(manifest.get('packages.extra', '').split() or ())
|
||||||
|
_core._setup_stop_after = "commandline"
|
||||||
|
try:
|
||||||
|
dist = _core.setup(**kwargs)
|
||||||
|
finally:
|
||||||
|
_core._setup_stop_after = None
|
||||||
|
|
||||||
|
result = ['MANIFEST', 'PKG-INFO', 'setup.py'] + list(config)
|
||||||
|
# TODO: work with default values:
|
||||||
|
for key in ('classifiers', 'description', 'summary', 'provides',
|
||||||
|
'license'):
|
||||||
|
filename = docs.get('meta.' + key, '').strip()
|
||||||
|
if filename and _os.path.isfile(filename):
|
||||||
|
result.append(filename)
|
||||||
|
|
||||||
|
cmd = dist.get_command_obj("build_py")
|
||||||
|
cmd.ensure_finalized()
|
||||||
|
#from pprint import pprint; pprint(("build_py", cmd.get_source_files()))
|
||||||
|
for item in cmd.get_source_files():
|
||||||
|
result.append(_posixpath.sep.join(
|
||||||
|
_os.path.normpath(item).split(_os.path.sep)
|
||||||
|
))
|
||||||
|
|
||||||
|
cmd = dist.get_command_obj("build_ext")
|
||||||
|
cmd.ensure_finalized()
|
||||||
|
#from pprint import pprint; pprint(("build_ext", cmd.get_source_files()))
|
||||||
|
for item in cmd.get_source_files():
|
||||||
|
result.append(_posixpath.sep.join(
|
||||||
|
_os.path.normpath(item).split(_os.path.sep)
|
||||||
|
))
|
||||||
|
for ext in cmd.extensions:
|
||||||
|
if ext.depends:
|
||||||
|
result.extend([_posixpath.sep.join(
|
||||||
|
_os.path.normpath(item).split(_os.path.sep)
|
||||||
|
) for item in ext.depends])
|
||||||
|
|
||||||
|
cmd = dist.get_command_obj("build_clib")
|
||||||
|
cmd.ensure_finalized()
|
||||||
|
if cmd.libraries:
|
||||||
|
#import pprint; pprint.pprint(("build_clib", cmd.get_source_files()))
|
||||||
|
for item in cmd.get_source_files():
|
||||||
|
result.append(_posixpath.sep.join(
|
||||||
|
_os.path.normpath(item).split(_os.path.sep)
|
||||||
|
))
|
||||||
|
for lib in cmd.libraries:
|
||||||
|
if lib[1].get('depends'):
|
||||||
|
result.extend([_posixpath.sep.join(
|
||||||
|
_os.path.normpath(item).split(_os.path.sep)
|
||||||
|
) for item in lib[1]['depends']])
|
||||||
|
|
||||||
|
cmd = dist.get_command_obj("build_scripts")
|
||||||
|
cmd.ensure_finalized()
|
||||||
|
#import pprint; pprint.pprint(("build_scripts", cmd.get_source_files()))
|
||||||
|
if cmd.get_source_files():
|
||||||
|
for item in cmd.get_source_files():
|
||||||
|
result.append(_posixpath.sep.join(
|
||||||
|
_os.path.normpath(item).split(_os.path.sep)
|
||||||
|
))
|
||||||
|
|
||||||
|
cmd = dist.get_command_obj("install_data")
|
||||||
|
cmd.ensure_finalized()
|
||||||
|
#from pprint import pprint; pprint(("install_data", cmd.get_inputs()))
|
||||||
|
try:
|
||||||
|
strings = basestring
|
||||||
|
except NameError:
|
||||||
|
strings = (str, unicode)
|
||||||
|
|
||||||
|
for item in cmd.get_inputs():
|
||||||
|
if isinstance(item, strings):
|
||||||
|
result.append(item)
|
||||||
|
else:
|
||||||
|
result.extend(item[1])
|
||||||
|
|
||||||
|
for item in manifest.get('dist', '').split():
|
||||||
|
result.append(item)
|
||||||
|
if _os.path.isdir(item):
|
||||||
|
for filename in _shell.files(item):
|
||||||
|
result.append(filename)
|
||||||
|
|
||||||
|
result = dict([(item, None) for item in result]).keys()
|
||||||
|
result.sort()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def run(config=('package.cfg',), ext=None, script_args=None, manifest_only=0):
|
||||||
|
""" Main runner """
|
||||||
|
if ext is None:
|
||||||
|
ext = []
|
||||||
|
|
||||||
|
cfg = _util.SafeConfigParser()
|
||||||
|
cfg.read(config)
|
||||||
|
pkg = dict(cfg.items('package'))
|
||||||
|
python_min = pkg.get('python.min') or None
|
||||||
|
python_max = pkg.get('python.max') or None
|
||||||
|
check_python_version('python', python_min, python_max)
|
||||||
|
pypy_min = pkg.get('pypy.min') or None
|
||||||
|
pypy_max = pkg.get('pypy.max') or None
|
||||||
|
check_python_version('pypy', pypy_min, pypy_max)
|
||||||
|
jython_min = pkg.get('jython.min') or None
|
||||||
|
jython_max = pkg.get('jython.max') or None
|
||||||
|
check_python_version('jython', jython_min, jython_max)
|
||||||
|
|
||||||
|
manifest = dict(cfg.items('manifest'))
|
||||||
|
try:
|
||||||
|
docs = dict(cfg.items('docs'))
|
||||||
|
except _config_parser.NoSectionError:
|
||||||
|
docs = {}
|
||||||
|
|
||||||
|
summary, description = find_description(docs)
|
||||||
|
scripts = manifest.get('scripts', '').strip() or None
|
||||||
|
if scripts:
|
||||||
|
scripts = scripts.split()
|
||||||
|
modules = manifest.get('modules', '').strip() or None
|
||||||
|
if modules:
|
||||||
|
modules = modules.split()
|
||||||
|
keywords = docs.get('meta.keywords', '').strip() or None
|
||||||
|
if keywords:
|
||||||
|
keywords = keywords.split()
|
||||||
|
revision = pkg.get('version.revision', '').strip()
|
||||||
|
if revision:
|
||||||
|
revision = "-r%s" % (revision,)
|
||||||
|
|
||||||
|
kwargs = {
|
||||||
|
'name': pkg['name'],
|
||||||
|
'version': "%s%s" % (
|
||||||
|
pkg['version.number'],
|
||||||
|
["", "-dev%s" % (revision,)][_util.humanbool(
|
||||||
|
'version.dev', pkg.get('version.dev', 'false')
|
||||||
|
)],
|
||||||
|
),
|
||||||
|
'provides': find_provides(docs),
|
||||||
|
'description': summary,
|
||||||
|
'long_description': description,
|
||||||
|
'classifiers': find_classifiers(docs),
|
||||||
|
'keywords': keywords,
|
||||||
|
'author': pkg['author.name'],
|
||||||
|
'author_email': pkg['author.email'],
|
||||||
|
'maintainer': pkg.get('maintainer.name'),
|
||||||
|
'maintainer_email': pkg.get('maintainer.email'),
|
||||||
|
'url': pkg.get('url.homepage'),
|
||||||
|
'download_url': pkg.get('url.download'),
|
||||||
|
'license': find_license(docs),
|
||||||
|
'package_dir': {'': manifest.get('packages.lib', '.')},
|
||||||
|
'packages': find_packages(manifest),
|
||||||
|
'py_modules': modules,
|
||||||
|
'ext_modules': ext,
|
||||||
|
'scripts': scripts,
|
||||||
|
'script_args': script_args,
|
||||||
|
'data_files': find_data(pkg['name'], docs),
|
||||||
|
'cmdclass': {
|
||||||
|
'build' : _commands.Build,
|
||||||
|
'build_ext' : _commands.BuildExt,
|
||||||
|
'install' : _commands.Install,
|
||||||
|
'install_data': _commands.InstallData,
|
||||||
|
'install_lib' : _commands.InstallLib,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for key in ('provides',):
|
||||||
|
if key not in _core.setup_keywords:
|
||||||
|
del kwargs[key]
|
||||||
|
|
||||||
|
if manifest_only:
|
||||||
|
return make_manifest(manifest, config, docs, kwargs)
|
||||||
|
|
||||||
|
# monkey-patch crappy manifest writer away.
|
||||||
|
from distutils.command import sdist
|
||||||
|
sdist.sdist.get_file_list = sdist.sdist.read_manifest
|
||||||
|
|
||||||
|
return _core.setup(**kwargs)
|
478
_setup/py2/shell.py
Normal file
478
_setup/py2/shell.py
Normal file
@ -0,0 +1,478 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007 - 2013
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=================
|
||||||
|
Shell utilities
|
||||||
|
=================
|
||||||
|
|
||||||
|
Shell utilities.
|
||||||
|
"""
|
||||||
|
from __future__ import generators
|
||||||
|
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import errno as _errno
|
||||||
|
import fnmatch as _fnmatch
|
||||||
|
import os as _os
|
||||||
|
import shutil as _shutil
|
||||||
|
import sys as _sys
|
||||||
|
import tempfile as _tempfile
|
||||||
|
|
||||||
|
cwd = _os.path.dirname(_os.path.abspath(_sys.argv[0]))
|
||||||
|
|
||||||
|
class ExitError(RuntimeError):
|
||||||
|
""" Exit error """
|
||||||
|
def __init__(self, code):
|
||||||
|
RuntimeError.__init__(self, code)
|
||||||
|
self.code = code
|
||||||
|
self.signal = None
|
||||||
|
|
||||||
|
|
||||||
|
class SignalError(ExitError):
|
||||||
|
""" Signal error """
|
||||||
|
def __init__(self, code, signal):
|
||||||
|
ExitError.__init__(self, code)
|
||||||
|
import signal as _signal
|
||||||
|
self.signal = signal
|
||||||
|
for key, val in vars(_signal).iteritems():
|
||||||
|
if key.startswith('SIG') and not key.startswith('SIG_'):
|
||||||
|
if val == signal:
|
||||||
|
self.signalstr = key[3:]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.signalstr = '%04d' % signal
|
||||||
|
|
||||||
|
|
||||||
|
def native(path):
|
||||||
|
""" Convert slash path to native """
|
||||||
|
path = _os.path.sep.join(path.split('/'))
|
||||||
|
return _os.path.normpath(_os.path.join(cwd, path))
|
||||||
|
|
||||||
|
|
||||||
|
def cp(src, dest):
|
||||||
|
""" Copy src to dest """
|
||||||
|
_shutil.copy2(native(src), native(dest))
|
||||||
|
|
||||||
|
|
||||||
|
def cp_r(src, dest):
|
||||||
|
""" Copy -r src to dest """
|
||||||
|
_shutil.copytree(native(src), native(dest))
|
||||||
|
|
||||||
|
|
||||||
|
def rm(dest):
|
||||||
|
""" Remove a file """
|
||||||
|
try:
|
||||||
|
_os.unlink(native(dest))
|
||||||
|
except OSError, e:
|
||||||
|
if _errno.ENOENT != e.errno:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def rm_rf(dest):
|
||||||
|
""" Remove a tree """
|
||||||
|
dest = native(dest)
|
||||||
|
if _os.path.exists(dest):
|
||||||
|
for path in files(dest, '*'):
|
||||||
|
_os.chmod(native(path), 0644)
|
||||||
|
_shutil.rmtree(dest)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
mkstemp = _tempfile.mkstemp
|
||||||
|
except AttributeError:
|
||||||
|
# helpers stolen from 2.4 tempfile module
|
||||||
|
try:
|
||||||
|
import fcntl as _fcntl
|
||||||
|
except ImportError:
|
||||||
|
def _set_cloexec(fd):
|
||||||
|
""" Set close-on-exec (not implemented, but not an error) """
|
||||||
|
# pylint: disable = W0613
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
def _set_cloexec(fd):
|
||||||
|
""" Set close-on-exec """
|
||||||
|
try:
|
||||||
|
flags = _fcntl.fcntl(fd, _fcntl.F_GETFD, 0)
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# flags read successfully, modify
|
||||||
|
flags |= _fcntl.FD_CLOEXEC
|
||||||
|
_fcntl.fcntl(fd, _fcntl.F_SETFD, flags)
|
||||||
|
|
||||||
|
_text_openflags = _os.O_RDWR | _os.O_CREAT | _os.O_EXCL
|
||||||
|
_text_openflags |= getattr(_os, 'O_NOINHERIT', 0)
|
||||||
|
_text_openflags |= getattr(_os, 'O_NOFOLLOW', 0)
|
||||||
|
|
||||||
|
_bin_openflags = _text_openflags
|
||||||
|
_bin_openflags |= getattr(_os, 'O_BINARY', 0)
|
||||||
|
|
||||||
|
def mkstemp(suffix="", prefix=_tempfile.gettempprefix(), dir=None,
|
||||||
|
text=False):
|
||||||
|
""" Create secure temp file """
|
||||||
|
# pylint: disable = W0622
|
||||||
|
if dir is None:
|
||||||
|
dir = _tempfile.gettempdir()
|
||||||
|
if text:
|
||||||
|
flags = _text_openflags
|
||||||
|
else:
|
||||||
|
flags = _bin_openflags
|
||||||
|
count = 100
|
||||||
|
while count > 0:
|
||||||
|
j = _tempfile._counter.get_next() # pylint: disable = E1101, W0212
|
||||||
|
fname = _os.path.join(dir, prefix + str(j) + suffix)
|
||||||
|
try:
|
||||||
|
fd = _os.open(fname, flags, 0600)
|
||||||
|
except OSError, e:
|
||||||
|
if e.errno == _errno.EEXIST:
|
||||||
|
count -= 1
|
||||||
|
continue
|
||||||
|
raise
|
||||||
|
_set_cloexec(fd)
|
||||||
|
return fd, _os.path.abspath(fname)
|
||||||
|
raise IOError, (_errno.EEXIST, "No usable temporary file name found")
|
||||||
|
|
||||||
|
|
||||||
|
def _pipespawn(argv, env):
|
||||||
|
""" Pipe spawn """
|
||||||
|
# pylint: disable = R0912
|
||||||
|
import pickle as _pickle
|
||||||
|
fd, name = mkstemp('.py')
|
||||||
|
try:
|
||||||
|
_os.write(fd, (r"""
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
try:
|
||||||
|
import subprocess
|
||||||
|
except ImportError:
|
||||||
|
subprocess = None
|
||||||
|
import sys
|
||||||
|
|
||||||
|
argv = pickle.loads(%(argv)s)
|
||||||
|
env = pickle.loads(%(env)s)
|
||||||
|
if 'X_JYTHON_WA_PATH' in env:
|
||||||
|
env['PATH'] = env['X_JYTHON_WA_PATH']
|
||||||
|
|
||||||
|
if subprocess is None:
|
||||||
|
pid = os.spawnve(os.P_NOWAIT, argv[0], argv, env)
|
||||||
|
result = os.waitpid(pid, 0)[1]
|
||||||
|
else:
|
||||||
|
p = subprocess.Popen(argv, env=env)
|
||||||
|
result = p.wait()
|
||||||
|
if result < 0:
|
||||||
|
print "\n%%d 1" %% (-result)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
if result == 0:
|
||||||
|
sys.exit(0)
|
||||||
|
signalled = getattr(os, 'WIFSIGNALED', None)
|
||||||
|
if signalled is not None:
|
||||||
|
if signalled(result):
|
||||||
|
print "\n%%d %%d" %% (os.WTERMSIG(result), result & 7)
|
||||||
|
sys.exit(2)
|
||||||
|
print "\n%%d" %% (result & 7,)
|
||||||
|
sys.exit(3)
|
||||||
|
""".strip() + "\n") % {
|
||||||
|
'argv': repr(_pickle.dumps(argv)),
|
||||||
|
'env': repr(_pickle.dumps(env)),
|
||||||
|
})
|
||||||
|
fd, _ = None, _os.close(fd)
|
||||||
|
if _sys.platform == 'win32':
|
||||||
|
argv = []
|
||||||
|
for arg in [_sys.executable, name]:
|
||||||
|
if ' ' in arg or arg.startswith('"'):
|
||||||
|
arg = '"%s"' % arg.replace('"', '\\"')
|
||||||
|
argv.append(arg)
|
||||||
|
argv = ' '.join(argv)
|
||||||
|
shell = True
|
||||||
|
close_fds = False
|
||||||
|
else:
|
||||||
|
argv = [_sys.executable, name]
|
||||||
|
shell = False
|
||||||
|
close_fds = True
|
||||||
|
|
||||||
|
res = 0
|
||||||
|
try:
|
||||||
|
import subprocess
|
||||||
|
except ImportError:
|
||||||
|
import popen2 as _popen2
|
||||||
|
proc = _popen2.Popen3(argv, False)
|
||||||
|
try:
|
||||||
|
proc.tochild.close()
|
||||||
|
result = proc.fromchild.read()
|
||||||
|
finally:
|
||||||
|
res = proc.wait()
|
||||||
|
else:
|
||||||
|
if 'X_JYTHON_WA_PATH' in env:
|
||||||
|
env['PATH'] = env['X_JYTHON_WA_PATH']
|
||||||
|
|
||||||
|
proc = subprocess.Popen(argv,
|
||||||
|
shell=shell,
|
||||||
|
stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
close_fds=close_fds,
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
proc.stdin.close()
|
||||||
|
result = proc.stdout.read()
|
||||||
|
finally:
|
||||||
|
res = proc.wait()
|
||||||
|
if res != 0:
|
||||||
|
if res == 2:
|
||||||
|
signal, code = map(int, result.splitlines()[-1].split())
|
||||||
|
raise SignalError(code, signal)
|
||||||
|
elif res == 3:
|
||||||
|
code = int(result.splitlines()[-1].strip())
|
||||||
|
raise ExitError(code)
|
||||||
|
raise ExitError(res)
|
||||||
|
|
||||||
|
return result
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
if fd is not None:
|
||||||
|
_os.close(fd)
|
||||||
|
finally:
|
||||||
|
_os.unlink(name)
|
||||||
|
|
||||||
|
|
||||||
|
def _filepipespawn(infile, outfile, argv, env):
|
||||||
|
""" File Pipe spawn """
|
||||||
|
try:
|
||||||
|
import subprocess
|
||||||
|
except ImportError:
|
||||||
|
subprocess = None
|
||||||
|
import pickle as _pickle
|
||||||
|
fd, name = mkstemp('.py')
|
||||||
|
try:
|
||||||
|
_os.write(fd, ("""
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
import sys
|
||||||
|
|
||||||
|
infile = pickle.loads(%(infile)s)
|
||||||
|
outfile = pickle.loads(%(outfile)s)
|
||||||
|
argv = pickle.loads(%(argv)s)
|
||||||
|
env = pickle.loads(%(env)s)
|
||||||
|
|
||||||
|
if infile is not None:
|
||||||
|
infile = open(infile, 'rb')
|
||||||
|
os.dup2(infile.fileno(), 0)
|
||||||
|
infile.close()
|
||||||
|
if outfile is not None:
|
||||||
|
outfile = open(outfile, 'wb')
|
||||||
|
os.dup2(outfile.fileno(), 1)
|
||||||
|
outfile.close()
|
||||||
|
|
||||||
|
pid = os.spawnve(os.P_NOWAIT, argv[0], argv, env)
|
||||||
|
result = os.waitpid(pid, 0)[1]
|
||||||
|
sys.exit(result & 7)
|
||||||
|
""".strip() + "\n") % {
|
||||||
|
'infile': repr(_pickle.dumps(_os.path.abspath(infile))),
|
||||||
|
'outfile': repr(_pickle.dumps(_os.path.abspath(outfile))),
|
||||||
|
'argv': repr(_pickle.dumps(argv)),
|
||||||
|
'env': repr(_pickle.dumps(env)),
|
||||||
|
})
|
||||||
|
fd, _ = None, _os.close(fd)
|
||||||
|
if _sys.platform == 'win32':
|
||||||
|
argv = []
|
||||||
|
for arg in [_sys.executable, name]:
|
||||||
|
if ' ' in arg or arg.startswith('"'):
|
||||||
|
arg = '"%s"' % arg.replace('"', '\\"')
|
||||||
|
argv.append(arg)
|
||||||
|
argv = ' '.join(argv)
|
||||||
|
close_fds = False
|
||||||
|
shell = True
|
||||||
|
else:
|
||||||
|
argv = [_sys.executable, name]
|
||||||
|
close_fds = True
|
||||||
|
shell = False
|
||||||
|
|
||||||
|
if subprocess is None:
|
||||||
|
pid = _os.spawnve(_os.P_NOWAIT, argv[0], argv, env)
|
||||||
|
return _os.waitpid(pid, 0)[1]
|
||||||
|
else:
|
||||||
|
p = subprocess.Popen(
|
||||||
|
argv, env=env, shell=shell, close_fds=close_fds
|
||||||
|
)
|
||||||
|
return p.wait()
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
if fd is not None:
|
||||||
|
_os.close(fd)
|
||||||
|
finally:
|
||||||
|
_os.unlink(name)
|
||||||
|
|
||||||
|
|
||||||
|
def spawn(*argv, **kwargs):
|
||||||
|
""" Spawn a process """
|
||||||
|
try:
|
||||||
|
import subprocess
|
||||||
|
except ImportError:
|
||||||
|
subprocess = None
|
||||||
|
|
||||||
|
if _sys.platform == 'win32':
|
||||||
|
newargv = []
|
||||||
|
for arg in argv:
|
||||||
|
if not arg or ' ' in arg or arg.startswith('"'):
|
||||||
|
arg = '"%s"' % arg.replace('"', '\\"')
|
||||||
|
newargv.append(arg)
|
||||||
|
argv = newargv
|
||||||
|
close_fds = False
|
||||||
|
shell = True
|
||||||
|
else:
|
||||||
|
close_fds = True
|
||||||
|
shell = False
|
||||||
|
|
||||||
|
env = kwargs.get('env')
|
||||||
|
if env is None:
|
||||||
|
env = dict(_os.environ)
|
||||||
|
if 'X_JYTHON_WA_PATH' in env:
|
||||||
|
env['PATH'] = env['X_JYTHON_WA_PATH']
|
||||||
|
|
||||||
|
echo = kwargs.get('echo')
|
||||||
|
if echo:
|
||||||
|
print ' '.join(argv)
|
||||||
|
filepipe = kwargs.get('filepipe')
|
||||||
|
if filepipe:
|
||||||
|
return _filepipespawn(
|
||||||
|
kwargs.get('stdin'), kwargs.get('stdout'), argv, env
|
||||||
|
)
|
||||||
|
pipe = kwargs.get('stdout')
|
||||||
|
if pipe:
|
||||||
|
return _pipespawn(argv, env)
|
||||||
|
|
||||||
|
if subprocess is None:
|
||||||
|
pid = _os.spawnve(_os.P_NOWAIT, argv[0], argv, env)
|
||||||
|
return _os.waitpid(pid, 0)[1]
|
||||||
|
else:
|
||||||
|
p = subprocess.Popen(argv, env=env, shell=shell, close_fds=close_fds)
|
||||||
|
return p.wait()
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
walk = _os.walk
|
||||||
|
except AttributeError:
|
||||||
|
# copy from python 2.4 sources (modulo docs and comments)
|
||||||
|
def walk(top, topdown=True, onerror=None):
|
||||||
|
""" directory tree walker """
|
||||||
|
# pylint: disable = C0103
|
||||||
|
join, isdir, islink = _os.path.join, _os.path.isdir, _os.path.islink
|
||||||
|
listdir, error = _os.listdir, _os.error
|
||||||
|
|
||||||
|
try:
|
||||||
|
names = listdir(top)
|
||||||
|
except error, err:
|
||||||
|
if onerror is not None:
|
||||||
|
onerror(err)
|
||||||
|
return
|
||||||
|
|
||||||
|
dirs, nondirs = [], []
|
||||||
|
for name in names:
|
||||||
|
if isdir(join(top, name)):
|
||||||
|
dirs.append(name)
|
||||||
|
else:
|
||||||
|
nondirs.append(name)
|
||||||
|
|
||||||
|
if topdown:
|
||||||
|
yield top, dirs, nondirs
|
||||||
|
for name in dirs:
|
||||||
|
path = join(top, name)
|
||||||
|
if not islink(path):
|
||||||
|
for x in walk(path, topdown, onerror):
|
||||||
|
yield x
|
||||||
|
if not topdown:
|
||||||
|
yield top, dirs, nondirs
|
||||||
|
|
||||||
|
|
||||||
|
def files(base, wildcard='[!.]*', recursive=1, prune=('.git', '.svn', 'CVS')):
|
||||||
|
""" Determine a filelist """
|
||||||
|
for dirpath, dirnames, filenames in walk(native(base)):
|
||||||
|
for item in prune:
|
||||||
|
if item in dirnames:
|
||||||
|
dirnames.remove(item)
|
||||||
|
|
||||||
|
filenames.sort()
|
||||||
|
for name in _fnmatch.filter(filenames, wildcard):
|
||||||
|
dest = _os.path.join(dirpath, name)
|
||||||
|
if dest.startswith(cwd):
|
||||||
|
dest = dest.replace(cwd, '', 1)
|
||||||
|
aslist = []
|
||||||
|
head, tail = _os.path.split(dest)
|
||||||
|
while tail:
|
||||||
|
aslist.append(tail)
|
||||||
|
head, tail = _os.path.split(head)
|
||||||
|
aslist.reverse()
|
||||||
|
dest = '/'.join(aslist)
|
||||||
|
yield dest
|
||||||
|
|
||||||
|
if not recursive:
|
||||||
|
break
|
||||||
|
dirnames.sort()
|
||||||
|
|
||||||
|
|
||||||
|
def dirs(base, wildcard='[!.]*', recursive=1, prune=('.git', '.svn', 'CVS')):
|
||||||
|
""" Determine a filelist """
|
||||||
|
for dirpath, dirnames, filenames in walk(native(base)):
|
||||||
|
for item in prune:
|
||||||
|
if item in dirnames:
|
||||||
|
dirnames.remove(item)
|
||||||
|
|
||||||
|
dirnames.sort()
|
||||||
|
for name in _fnmatch.filter(dirnames, wildcard):
|
||||||
|
dest = _os.path.join(dirpath, name)
|
||||||
|
if dest.startswith(cwd):
|
||||||
|
dest = dest.replace(cwd, '', 1)
|
||||||
|
aslist = []
|
||||||
|
head, tail = _os.path.split(dest)
|
||||||
|
while tail:
|
||||||
|
aslist.append(tail)
|
||||||
|
head, tail = _os.path.split(head)
|
||||||
|
aslist.reverse()
|
||||||
|
dest = '/'.join(aslist)
|
||||||
|
yield dest
|
||||||
|
|
||||||
|
if not recursive:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def frompath(executable):
|
||||||
|
""" Find executable in PATH """
|
||||||
|
# Based on distutils.spawn.find_executable.
|
||||||
|
path = _os.environ.get('PATH', '')
|
||||||
|
paths = [
|
||||||
|
_os.path.expanduser(item)
|
||||||
|
for item in path.split(_os.pathsep)
|
||||||
|
]
|
||||||
|
ext = _os.path.splitext(executable)[1]
|
||||||
|
exts = ['']
|
||||||
|
if _sys.platform == 'win32' or _os.name == 'os2':
|
||||||
|
eext = ['.exe', '.bat', '.py']
|
||||||
|
if ext not in eext:
|
||||||
|
exts.extend(eext)
|
||||||
|
|
||||||
|
for ext in exts:
|
||||||
|
if not _os.path.isfile(executable + ext):
|
||||||
|
for path in paths:
|
||||||
|
fname = _os.path.join(path, executable + ext)
|
||||||
|
if _os.path.isfile(fname):
|
||||||
|
# the file exists, we have a shot at spawn working
|
||||||
|
return fname
|
||||||
|
else:
|
||||||
|
return executable + ext
|
||||||
|
|
||||||
|
return None
|
28
_setup/py2/term/__init__.py
Normal file
28
_setup/py2/term/__init__.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=====================
|
||||||
|
Package _setup.term
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Terminal tools, not distributed.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
# pylint: disable = W0611
|
||||||
|
from _setup.term._term import terminfo, write, green, red, yellow, announce
|
115
_setup/py2/term/_term.py
Normal file
115
_setup/py2/term/_term.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=================
|
||||||
|
Terminal writer
|
||||||
|
=================
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
|
||||||
|
class _INFO(dict):
|
||||||
|
""" Terminal info dict """
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
""" Initialization """
|
||||||
|
dict.__init__(self, {
|
||||||
|
'NORMAL': '',
|
||||||
|
'BOLD': '',
|
||||||
|
'ERASE': '\n',
|
||||||
|
'RED': '',
|
||||||
|
'YELLOW': '',
|
||||||
|
'GREEN': '',
|
||||||
|
})
|
||||||
|
try:
|
||||||
|
import curses as _curses
|
||||||
|
except ImportError:
|
||||||
|
# fixup if a submodule of curses failed.
|
||||||
|
if 'curses' in _sys.modules:
|
||||||
|
del _sys.modules['curses']
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
_curses.setupterm()
|
||||||
|
except (TypeError, _curses.error):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
def make_color(color):
|
||||||
|
""" Make color control string """
|
||||||
|
seq = _curses.tigetstr('setaf')
|
||||||
|
if seq is not None:
|
||||||
|
# XXX may fail - need better logic
|
||||||
|
seq = seq.replace("%p1", "") % color
|
||||||
|
return seq
|
||||||
|
|
||||||
|
self['NORMAL'] = _curses.tigetstr('sgr0')
|
||||||
|
self['BOLD'] = _curses.tigetstr('bold')
|
||||||
|
|
||||||
|
erase = _curses.tigetstr('el1')
|
||||||
|
if erase is not None:
|
||||||
|
self['ERASE'] = erase + _curses.tigetstr('cr')
|
||||||
|
|
||||||
|
self['RED'] = make_color(_curses.COLOR_RED)
|
||||||
|
self['YELLOW'] = make_color(_curses.COLOR_YELLOW)
|
||||||
|
self['GREEN'] = make_color(_curses.COLOR_GREEN)
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
""" Deliver always """
|
||||||
|
dict.get(self, key) or ""
|
||||||
|
|
||||||
|
|
||||||
|
def terminfo():
|
||||||
|
""" Get info singleton """
|
||||||
|
# pylint: disable = E1101, W0612
|
||||||
|
if terminfo.info is None:
|
||||||
|
terminfo.info = _INFO()
|
||||||
|
return terminfo.info
|
||||||
|
terminfo.info = None
|
||||||
|
|
||||||
|
|
||||||
|
def write(fmt, **kwargs):
|
||||||
|
""" Write stuff on the terminal """
|
||||||
|
parm = dict(terminfo())
|
||||||
|
parm.update(kwargs)
|
||||||
|
_sys.stdout.write(fmt % parm)
|
||||||
|
_sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def green(bmt, **kwargs):
|
||||||
|
""" Write something in green on screen """
|
||||||
|
announce("%%(GREEN)s%s%%(NORMAL)s" % bmt, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def red(bmt, **kwargs):
|
||||||
|
""" Write something in red on the screen """
|
||||||
|
announce("%%(BOLD)s%%(RED)s%s%%(NORMAL)s" % bmt, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def yellow(fmt, **kwargs):
|
||||||
|
""" Write something in yellow on the screen """
|
||||||
|
announce("%%(BOLD)s%%(YELLOW)s%s%%(NORMAL)s" % fmt, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def announce(fmt, **kwargs):
|
||||||
|
""" Announce something """
|
||||||
|
write(fmt, **kwargs)
|
||||||
|
_sys.stdout.write("\n")
|
||||||
|
_sys.stdout.flush()
|
||||||
|
|
||||||
|
|
73
_setup/py2/util.py
Normal file
73
_setup/py2/util.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=================
|
||||||
|
Setup utilities
|
||||||
|
=================
|
||||||
|
|
||||||
|
Setup utilities.
|
||||||
|
"""
|
||||||
|
__author__ = u"Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
try:
|
||||||
|
from distutils import log
|
||||||
|
except ImportError:
|
||||||
|
class log(object):
|
||||||
|
def info(self, value):
|
||||||
|
print value
|
||||||
|
def debug(self, value):
|
||||||
|
pass
|
||||||
|
log = log()
|
||||||
|
|
||||||
|
from distutils import util as _util
|
||||||
|
try:
|
||||||
|
from ConfigParser import SafeConfigParser
|
||||||
|
except ImportError:
|
||||||
|
import ConfigParser as _config_parser
|
||||||
|
class SafeConfigParser(_config_parser.ConfigParser):
|
||||||
|
""" Safe config parser """
|
||||||
|
def _interpolate(self, section, option, rawval, vars):
|
||||||
|
return rawval
|
||||||
|
|
||||||
|
def items(self, section):
|
||||||
|
return [(key, self.get(section, key))
|
||||||
|
for key in self.options(section)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def humanbool(name, value):
|
||||||
|
"""
|
||||||
|
Determine human boolean value
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`name` : ``str``
|
||||||
|
The config key (used for error message)
|
||||||
|
|
||||||
|
`value` : ``str``
|
||||||
|
The config value
|
||||||
|
|
||||||
|
:Return: The boolean value
|
||||||
|
:Rtype: ``bool``
|
||||||
|
|
||||||
|
:Exceptions:
|
||||||
|
- `ValueError` : The value could not be recognized
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return _util.strtobool(str(value).strip().lower() or 'no')
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError("Unrecognized config value: %s = %s" % (name, value))
|
27
_setup/py3/__init__.py
Normal file
27
_setup/py3/__init__.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
================
|
||||||
|
Package _setup
|
||||||
|
================
|
||||||
|
|
||||||
|
This package provides tools for main package setup.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
from _setup.setup import run # pylint: disable = W0611
|
266
_setup/py3/commands.py
Normal file
266
_setup/py3/commands.py
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
===================
|
||||||
|
Command extenders
|
||||||
|
===================
|
||||||
|
|
||||||
|
Command extenders.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
__test__ = False
|
||||||
|
|
||||||
|
from distutils import fancy_getopt as _fancy_getopt
|
||||||
|
from distutils import log
|
||||||
|
from distutils.command import build as _build
|
||||||
|
from distutils.command import build_ext as _build_ext
|
||||||
|
from distutils.command import install as _install
|
||||||
|
from distutils.command import install_data as _install_data
|
||||||
|
from distutils.command import install_lib as _install_lib
|
||||||
|
import os as _os
|
||||||
|
|
||||||
|
_option_defaults = {}
|
||||||
|
_option_inherits = {}
|
||||||
|
_option_finalizers = {}
|
||||||
|
_command_mapping = {
|
||||||
|
'install': 'Install',
|
||||||
|
'install_data': 'InstallData',
|
||||||
|
'install_lib': 'InstallLib',
|
||||||
|
'build': 'Build',
|
||||||
|
'build_ext': 'BuildExt',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def add_option(command, long_name, help_text, short_name=None, default=None,
|
||||||
|
inherit=None):
|
||||||
|
""" Add an option """
|
||||||
|
try:
|
||||||
|
command_class = globals()[_command_mapping[command]]
|
||||||
|
except KeyError:
|
||||||
|
raise ValueError("Unknown command %r" % (command,))
|
||||||
|
for opt in command_class.user_options:
|
||||||
|
if opt[0] == long_name:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
opt = (long_name, short_name, help_text)
|
||||||
|
command_class.user_options.append(opt)
|
||||||
|
if not long_name.endswith('='):
|
||||||
|
command_class.boolean_options.append(long_name)
|
||||||
|
attr_name = _fancy_getopt.translate_longopt(long_name)
|
||||||
|
else:
|
||||||
|
attr_name = _fancy_getopt.translate_longopt(long_name[:-1])
|
||||||
|
if command not in _option_defaults:
|
||||||
|
_option_defaults[command] = []
|
||||||
|
if inherit is not None:
|
||||||
|
if isinstance(inherit, str):
|
||||||
|
inherit = [inherit]
|
||||||
|
for i_inherit in inherit:
|
||||||
|
add_option(
|
||||||
|
i_inherit, long_name, help_text, short_name, default
|
||||||
|
)
|
||||||
|
default = None
|
||||||
|
if command not in _option_inherits:
|
||||||
|
_option_inherits[command] = []
|
||||||
|
for i_inherit in inherit:
|
||||||
|
for i_command, opt_name in _option_inherits[command]:
|
||||||
|
if i_command == i_inherit and opt_name == attr_name:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
_option_inherits[command].append((i_inherit, attr_name))
|
||||||
|
_option_defaults[command].append((attr_name, default))
|
||||||
|
|
||||||
|
|
||||||
|
def add_finalizer(command, key, func):
|
||||||
|
""" Add finalizer """
|
||||||
|
if command not in _option_finalizers:
|
||||||
|
_option_finalizers[command] = {}
|
||||||
|
if key not in _option_finalizers[command]:
|
||||||
|
_option_finalizers[command][key] = func
|
||||||
|
|
||||||
|
|
||||||
|
class Install(_install.install):
|
||||||
|
""" Extended installer to reflect the additional data options """
|
||||||
|
user_options = _install.install.user_options + [
|
||||||
|
('single-version-externally-managed', None,
|
||||||
|
"Compat option. Does not a thing."),
|
||||||
|
]
|
||||||
|
boolean_options = _install.install.boolean_options + [
|
||||||
|
'single-version-externally-managed'
|
||||||
|
]
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
""" Prepare for new options """
|
||||||
|
_install.install.initialize_options(self)
|
||||||
|
self.single_version_externally_managed = None
|
||||||
|
if 'install' in _option_defaults:
|
||||||
|
for opt_name, default in _option_defaults['install']:
|
||||||
|
setattr(self, opt_name, default)
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
""" Finalize options """
|
||||||
|
_install.install.finalize_options(self)
|
||||||
|
if 'install' in _option_inherits:
|
||||||
|
for parent, opt_name in _option_inherits['install']:
|
||||||
|
self.set_undefined_options(parent, (opt_name, opt_name))
|
||||||
|
if 'install' in _option_finalizers:
|
||||||
|
for func in list(_option_finalizers['install'].values()):
|
||||||
|
func(self)
|
||||||
|
|
||||||
|
|
||||||
|
class InstallData(_install_data.install_data):
|
||||||
|
""" Extended data installer """
|
||||||
|
user_options = _install_data.install_data.user_options + []
|
||||||
|
boolean_options = _install_data.install_data.boolean_options + []
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
""" Prepare for new options """
|
||||||
|
_install_data.install_data.initialize_options(self)
|
||||||
|
if 'install_data' in _option_defaults:
|
||||||
|
for opt_name, default in _option_defaults['install_data']:
|
||||||
|
setattr(self, opt_name, default)
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
""" Finalize options """
|
||||||
|
_install_data.install_data.finalize_options(self)
|
||||||
|
if 'install_data' in _option_inherits:
|
||||||
|
for parent, opt_name in _option_inherits['install_data']:
|
||||||
|
self.set_undefined_options(parent, (opt_name, opt_name))
|
||||||
|
if 'install_data' in _option_finalizers:
|
||||||
|
for func in list(_option_finalizers['install_data'].values()):
|
||||||
|
func(self)
|
||||||
|
|
||||||
|
|
||||||
|
class InstallLib(_install_lib.install_lib):
|
||||||
|
""" Extended lib installer """
|
||||||
|
user_options = _install_lib.install_lib.user_options + []
|
||||||
|
boolean_options = _install_lib.install_lib.boolean_options + []
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
""" Prepare for new options """
|
||||||
|
_install_lib.install_lib.initialize_options(self)
|
||||||
|
if 'install_lib' in _option_defaults:
|
||||||
|
for opt_name, default in _option_defaults['install_lib']:
|
||||||
|
setattr(self, opt_name, default)
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
""" Finalize options """
|
||||||
|
_install_lib.install_lib.finalize_options(self)
|
||||||
|
if 'install_lib' in _option_inherits:
|
||||||
|
for parent, opt_name in _option_inherits['install_lib']:
|
||||||
|
self.set_undefined_options(parent, (opt_name, opt_name))
|
||||||
|
if 'install_lib' in _option_finalizers:
|
||||||
|
for func in list(_option_finalizers['install_lib'].values()):
|
||||||
|
func(self)
|
||||||
|
|
||||||
|
|
||||||
|
class BuildExt(_build_ext.build_ext):
|
||||||
|
"""
|
||||||
|
Extended extension builder class
|
||||||
|
|
||||||
|
This class allows extensions to provide a ``check_prerequisites`` method
|
||||||
|
which is called before actually building it. The method takes the
|
||||||
|
`BuildExt` instance and returns whether the extension should be skipped or
|
||||||
|
not.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
""" Prepare for new options """
|
||||||
|
_build_ext.build_ext.initialize_options(self)
|
||||||
|
if 'build_ext' in _option_defaults:
|
||||||
|
for opt_name, default in _option_defaults['build_ext']:
|
||||||
|
setattr(self, opt_name, default)
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
""" Finalize options """
|
||||||
|
_build_ext.build_ext.finalize_options(self)
|
||||||
|
if 'build_ext' in _option_inherits:
|
||||||
|
for parent, opt_name in _option_inherits['build_ext']:
|
||||||
|
self.set_undefined_options(parent, (opt_name, opt_name))
|
||||||
|
if 'build_ext' in _option_finalizers:
|
||||||
|
for func in list(_option_finalizers['build_ext'].values()):
|
||||||
|
func(self)
|
||||||
|
|
||||||
|
def build_extension(self, ext):
|
||||||
|
"""
|
||||||
|
Build C extension - with extended functionality
|
||||||
|
|
||||||
|
The following features are added here:
|
||||||
|
|
||||||
|
- ``ext.check_prerequisites`` is called before the extension is being
|
||||||
|
built. See `Extension` for details. If the method does not exist,
|
||||||
|
simply no check will be run.
|
||||||
|
- The macros ``EXT_PACKAGE`` and ``EXT_MODULE`` will be filled (or
|
||||||
|
unset) depending on the extensions name, but only if they are not
|
||||||
|
already defined.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`ext` : `Extension`
|
||||||
|
The extension to build. If it's a pure
|
||||||
|
``distutils.core.Extension``, simply no prequisites check is
|
||||||
|
applied.
|
||||||
|
|
||||||
|
:Return: whatever ``distutils.command.build_ext.build_ext`` returns
|
||||||
|
:Rtype: any
|
||||||
|
"""
|
||||||
|
# handle name macros
|
||||||
|
macros = dict(ext.define_macros or ())
|
||||||
|
tup = ext.name.split('.')
|
||||||
|
if len(tup) == 1:
|
||||||
|
pkg, mod = None, tup[0]
|
||||||
|
else:
|
||||||
|
pkg, mod = '.'.join(tup[:-1]), tup[-1]
|
||||||
|
if pkg is not None and 'EXT_PACKAGE' not in macros:
|
||||||
|
ext.define_macros.append(('EXT_PACKAGE', pkg))
|
||||||
|
if 'EXT_MODULE' not in macros:
|
||||||
|
ext.define_macros.append(('EXT_MODULE', mod))
|
||||||
|
if pkg is None:
|
||||||
|
macros = dict(ext.undef_macros or ())
|
||||||
|
if 'EXT_PACKAGE' not in macros:
|
||||||
|
ext.undef_macros.append('EXT_PACKAGE')
|
||||||
|
|
||||||
|
# handle prereq checks
|
||||||
|
try:
|
||||||
|
checker = ext.check_prerequisites
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if checker(self):
|
||||||
|
log.info("Skipping %s extension" % ext.name)
|
||||||
|
return
|
||||||
|
|
||||||
|
return _build_ext.build_ext.build_extension(self, ext)
|
||||||
|
|
||||||
|
|
||||||
|
class Build(_build.build):
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
""" Prepare for new options """
|
||||||
|
_build.build.initialize_options(self)
|
||||||
|
if 'build' in _option_defaults:
|
||||||
|
for opt_name, default in _option_defaults['build']:
|
||||||
|
setattr(self, opt_name, default)
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
""" Finalize options """
|
||||||
|
_build.build.finalize_options(self)
|
||||||
|
if 'build' in _option_inherits:
|
||||||
|
for parent, opt_name in _option_inherits['build']:
|
||||||
|
self.set_undefined_options(parent, (opt_name, opt_name))
|
||||||
|
if 'build' in _option_finalizers:
|
||||||
|
for func in list(_option_finalizers['build'].values()):
|
||||||
|
func(self)
|
165
_setup/py3/data.py
Normal file
165
_setup/py3/data.py
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
===================
|
||||||
|
Data distribution
|
||||||
|
===================
|
||||||
|
|
||||||
|
This module provides tools to simplify data distribution.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
from distutils import filelist as _filelist
|
||||||
|
import os as _os
|
||||||
|
import posixpath as _posixpath
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import commands as _commands
|
||||||
|
|
||||||
|
|
||||||
|
def splitpath(path):
|
||||||
|
""" Split a path """
|
||||||
|
drive, path = '', _os.path.normpath(path)
|
||||||
|
try:
|
||||||
|
splitunc = _os.path.splitunc
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
drive, path = splitunc(path)
|
||||||
|
if not drive:
|
||||||
|
drive, path = _os.path.splitdrive(path)
|
||||||
|
elems = []
|
||||||
|
try:
|
||||||
|
sep = _os.path.sep
|
||||||
|
except AttributeError:
|
||||||
|
sep = _os.path.join('1', '2')[1:-1]
|
||||||
|
while 1:
|
||||||
|
prefix, path = _os.path.split(path)
|
||||||
|
elems.append(path)
|
||||||
|
if prefix in ('', sep):
|
||||||
|
drive = _os.path.join(drive, prefix)
|
||||||
|
break
|
||||||
|
path = prefix
|
||||||
|
elems.reverse()
|
||||||
|
return drive, elems
|
||||||
|
|
||||||
|
|
||||||
|
def finalizer(installer):
|
||||||
|
""" Finalize install_data """
|
||||||
|
data_files = []
|
||||||
|
for item in installer.data_files:
|
||||||
|
if not isinstance(item, Data):
|
||||||
|
data_files.append(item)
|
||||||
|
continue
|
||||||
|
data_files.extend(item.flatten(installer))
|
||||||
|
installer.data_files = data_files
|
||||||
|
|
||||||
|
|
||||||
|
class Data(object):
|
||||||
|
""" File list container """
|
||||||
|
|
||||||
|
def __init__(self, files, target=None, preserve=0, strip=0,
|
||||||
|
prefix=None):
|
||||||
|
""" Initialization """
|
||||||
|
self._files = files
|
||||||
|
self._target = target
|
||||||
|
self._preserve = preserve
|
||||||
|
self._strip = strip
|
||||||
|
self._prefix = prefix
|
||||||
|
self.fixup_commands()
|
||||||
|
|
||||||
|
def fixup_commands(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def from_templates(cls, *templates, **kwargs):
|
||||||
|
""" Initialize from template """
|
||||||
|
files = _filelist.FileList()
|
||||||
|
for tpl in templates:
|
||||||
|
for line in tpl.split(';'):
|
||||||
|
files.process_template_line(line.strip())
|
||||||
|
files.sort()
|
||||||
|
files.remove_duplicates()
|
||||||
|
result = []
|
||||||
|
for filename in files.files:
|
||||||
|
_, elems = splitpath(filename)
|
||||||
|
if '.svn' in elems or '.git' in elems:
|
||||||
|
continue
|
||||||
|
result.append(filename)
|
||||||
|
return cls(result, **kwargs)
|
||||||
|
from_templates = classmethod(from_templates)
|
||||||
|
|
||||||
|
def flatten(self, installer):
|
||||||
|
""" Flatten the file list to (target, file) tuples """
|
||||||
|
# pylint: disable = W0613
|
||||||
|
if self._prefix:
|
||||||
|
_, prefix = splitpath(self._prefix)
|
||||||
|
telems = prefix
|
||||||
|
else:
|
||||||
|
telems = []
|
||||||
|
|
||||||
|
tmap = {}
|
||||||
|
for fname in self._files:
|
||||||
|
(_, name), target = splitpath(fname), telems
|
||||||
|
if self._preserve:
|
||||||
|
if self._strip:
|
||||||
|
name = name[max(0, min(self._strip, len(name) - 1)):]
|
||||||
|
if len(name) > 1:
|
||||||
|
target = telems + name[:-1]
|
||||||
|
tmap.setdefault(_posixpath.join(*target), []).append(fname)
|
||||||
|
return list(tmap.items())
|
||||||
|
|
||||||
|
|
||||||
|
class Documentation(Data):
|
||||||
|
""" Documentation container """
|
||||||
|
|
||||||
|
def fixup_commands(self):
|
||||||
|
_commands.add_option('install_data', 'without-docs',
|
||||||
|
help_text='Do not install documentation files',
|
||||||
|
inherit='install',
|
||||||
|
)
|
||||||
|
_commands.add_finalizer('install_data', 'documentation', finalizer)
|
||||||
|
|
||||||
|
def flatten(self, installer):
|
||||||
|
""" Check if docs should be installed at all """
|
||||||
|
if installer.without_docs:
|
||||||
|
return []
|
||||||
|
return Data.flatten(self, installer)
|
||||||
|
|
||||||
|
|
||||||
|
class Manpages(Documentation):
|
||||||
|
""" Manpages container """
|
||||||
|
|
||||||
|
def dispatch(cls, files):
|
||||||
|
""" Automatically dispatch manpages to their target directories """
|
||||||
|
mpmap = {}
|
||||||
|
for manpage in files:
|
||||||
|
normalized = _os.path.normpath(manpage)
|
||||||
|
_, ext = _os.path.splitext(normalized)
|
||||||
|
if ext.startswith(_os.path.extsep):
|
||||||
|
ext = ext[len(_os.path.extsep):]
|
||||||
|
mpmap.setdefault(ext, []).append(manpage)
|
||||||
|
return [cls(manpages, prefix=_posixpath.join(
|
||||||
|
'share', 'man', 'man%s' % section,
|
||||||
|
)) for section, manpages in list(mpmap.items())]
|
||||||
|
dispatch = classmethod(dispatch)
|
||||||
|
|
||||||
|
def flatten(self, installer):
|
||||||
|
""" Check if manpages are suitable """
|
||||||
|
if _sys.platform == 'win32':
|
||||||
|
return []
|
||||||
|
return Documentation.flatten(self, installer)
|
25
_setup/py3/dev/__init__.py
Normal file
25
_setup/py3/dev/__init__.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
====================
|
||||||
|
Package _setup.dev
|
||||||
|
====================
|
||||||
|
|
||||||
|
Development tools, not distributed.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
258
_setup/py3/dev/_pylint.py
Normal file
258
_setup/py3/dev/_pylint.py
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2006, 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=================================
|
||||||
|
Support for code analysis tools
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Support for code analysis tools.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import re as _re
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import term as _term
|
||||||
|
from _setup import shell as _shell
|
||||||
|
|
||||||
|
|
||||||
|
class NotFinished(Exception):
|
||||||
|
""" Exception used for message passing in the stream filter """
|
||||||
|
|
||||||
|
class NotParseable(Exception):
|
||||||
|
""" Exception used for message passing in the stream filter """
|
||||||
|
|
||||||
|
class SpecialMessage(Exception):
|
||||||
|
""" Exception used for message passing in the stream filter """
|
||||||
|
|
||||||
|
|
||||||
|
class FilterStream(object):
|
||||||
|
""" Stream filter """
|
||||||
|
_LINERE = _re.compile(r'''
|
||||||
|
(?P<name>[^:]+)
|
||||||
|
:
|
||||||
|
(?P<lineno>\d+)
|
||||||
|
:\s+
|
||||||
|
\[(?P<mid>[^\],]+)(?:,\s+(?P<func>[^\]]+))?\]
|
||||||
|
\s+
|
||||||
|
(?P<desc>.*)
|
||||||
|
''', _re.X)
|
||||||
|
_SIMRE = _re.compile(r'in (?P<number>\d+) files')
|
||||||
|
_CYCRE = _re.compile(r'\((?P<cycle>[^)]+)\)')
|
||||||
|
|
||||||
|
def __init__(self, term, stream=_sys.stdout):
|
||||||
|
self.written = False
|
||||||
|
self._stream = stream
|
||||||
|
self._lastname = None
|
||||||
|
self._cycled = False
|
||||||
|
self._term = dict(term)
|
||||||
|
self._buffer = ''
|
||||||
|
|
||||||
|
def write(self, towrite):
|
||||||
|
""" Stream write function """
|
||||||
|
self._buffer += towrite
|
||||||
|
term = self._term
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
name, lineno, mid, func, desc = self._parse()
|
||||||
|
except NotFinished:
|
||||||
|
break
|
||||||
|
except SpecialMessage as e:
|
||||||
|
self._dospecial(e)
|
||||||
|
continue
|
||||||
|
except NotParseable as e:
|
||||||
|
self._print_literal(str(e.args[0]))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if name != self._lastname:
|
||||||
|
if self._lastname is not None:
|
||||||
|
self._stream.write("\n")
|
||||||
|
term['path'] = name
|
||||||
|
self._stream.write(
|
||||||
|
"%(BOLD)s>>> %(path)s%(NORMAL)s\n" % term
|
||||||
|
)
|
||||||
|
self._lastname = name
|
||||||
|
self.written = True
|
||||||
|
|
||||||
|
term['mid'] = mid
|
||||||
|
if mid.startswith('E') or mid.startswith('F'):
|
||||||
|
self._stream.write("%(BOLD)s%(RED)s%(mid)s%(NORMAL)s" % term)
|
||||||
|
elif mid == 'W0511':
|
||||||
|
self._stream.write(
|
||||||
|
"%(BOLD)s%(GREEN)s%(mid)s%(NORMAL)s" % term
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._stream.write(
|
||||||
|
"%(BOLD)s%(YELLOW)s%(mid)s%(NORMAL)s" % term
|
||||||
|
)
|
||||||
|
|
||||||
|
if int(lineno) != 0:
|
||||||
|
term['lineno'] = lineno
|
||||||
|
self._stream.write(" (%(lineno)s" % term)
|
||||||
|
if func:
|
||||||
|
term['func'] = func
|
||||||
|
self._stream.write(
|
||||||
|
", %(BOLD)s%(YELLOW)s%(func)s%(NORMAL)s" % term
|
||||||
|
)
|
||||||
|
self._stream.write(')')
|
||||||
|
|
||||||
|
self._stream.write(": %s\n" % desc)
|
||||||
|
self._stream.flush()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def _print_literal(self, line):
|
||||||
|
""" Print literal """
|
||||||
|
suppress = (
|
||||||
|
line.startswith('Unable to get imported names for ') or
|
||||||
|
line.startswith("Exception exceptions.RuntimeError: 'generator "
|
||||||
|
"ignored GeneratorExit' in <generator object at") or
|
||||||
|
line.startswith("Exception RuntimeError: 'generator "
|
||||||
|
"ignored GeneratorExit' in <generator object") or
|
||||||
|
not line.strip()
|
||||||
|
)
|
||||||
|
if not suppress:
|
||||||
|
self._stream.write("%s\n" % line)
|
||||||
|
self._stream.flush()
|
||||||
|
self.written = True
|
||||||
|
|
||||||
|
def _dospecial(self, e):
|
||||||
|
""" Deal with special messages """
|
||||||
|
if e.args[0] == 'R0401':
|
||||||
|
pos = self._buffer.find('\n')
|
||||||
|
line, self._buffer = (
|
||||||
|
self._buffer[:pos + 1], self._buffer[pos + 1:]
|
||||||
|
)
|
||||||
|
term = self._term
|
||||||
|
term['mid'] = e.args[0]
|
||||||
|
if not self._cycled:
|
||||||
|
self._cycled = True
|
||||||
|
self._stream.write('\n')
|
||||||
|
self._stream.write(
|
||||||
|
"%(BOLD)s%(YELLOW)s%(mid)s%(NORMAL)s" % term
|
||||||
|
)
|
||||||
|
self._stream.write(": Cyclic imports\n")
|
||||||
|
match = self._CYCRE.search(e.args[1])
|
||||||
|
term['cycle'] = match.group('cycle')
|
||||||
|
self._stream.write("%(BOLD)s@@@ %(NORMAL)s%(cycle)s\n" % term)
|
||||||
|
self._stream.flush()
|
||||||
|
self.written = True
|
||||||
|
|
||||||
|
elif e.args[0] == 'R0801':
|
||||||
|
match = self._SIMRE.search(e.args[1])
|
||||||
|
if not match:
|
||||||
|
raise AssertionError(
|
||||||
|
'Could not determine number of similar files'
|
||||||
|
)
|
||||||
|
|
||||||
|
numfiles = int(match.group('number'))
|
||||||
|
pos = -1
|
||||||
|
for _ in range(numfiles + 1):
|
||||||
|
pos = self._buffer.find('\n', pos + 1)
|
||||||
|
if pos >= 0:
|
||||||
|
lines = self._buffer[:pos + 1]
|
||||||
|
self._buffer = self._buffer[pos + 1:]
|
||||||
|
term = self._term
|
||||||
|
|
||||||
|
self._stream.write("\n")
|
||||||
|
for name in lines.splitlines()[1:]:
|
||||||
|
name = name.rstrip()[2:]
|
||||||
|
term['path'] = name
|
||||||
|
self._stream.write(
|
||||||
|
"%(BOLD)s=== %(path)s%(NORMAL)s\n" % term
|
||||||
|
)
|
||||||
|
self._lastname = name
|
||||||
|
|
||||||
|
term['mid'] = e.args[0]
|
||||||
|
self._stream.write(
|
||||||
|
"%(BOLD)s%(YELLOW)s%(mid)s%(NORMAL)s" % term
|
||||||
|
)
|
||||||
|
self._stream.write(": %s\n" % e.args[1])
|
||||||
|
self._stream.flush()
|
||||||
|
self.written = True
|
||||||
|
|
||||||
|
def _parse(self):
|
||||||
|
""" Parse output """
|
||||||
|
if '\n' not in self._buffer:
|
||||||
|
raise NotFinished()
|
||||||
|
|
||||||
|
line = self._buffer[:self._buffer.find('\n') + 1]
|
||||||
|
self._buffer = self._buffer[len(line):]
|
||||||
|
line = line.rstrip()
|
||||||
|
match = self._LINERE.match(line)
|
||||||
|
if not match:
|
||||||
|
raise NotParseable(line)
|
||||||
|
|
||||||
|
mid = match.group('mid')
|
||||||
|
if mid in ('R0801', 'R0401'):
|
||||||
|
self._buffer = "%s\n%s" % (line, self._buffer)
|
||||||
|
raise SpecialMessage(mid, match.group('desc'))
|
||||||
|
|
||||||
|
return match.group('name', 'lineno', 'mid', 'func', 'desc')
|
||||||
|
|
||||||
|
|
||||||
|
def run(config, *args):
|
||||||
|
""" Run pylint """
|
||||||
|
try:
|
||||||
|
from pylint import lint
|
||||||
|
from pylint.reporters import text
|
||||||
|
except ImportError:
|
||||||
|
return 2
|
||||||
|
|
||||||
|
if config is None:
|
||||||
|
config = _shell.native('pylint.conf')
|
||||||
|
argv = ['--rcfile', config,
|
||||||
|
'--reports', 'no',
|
||||||
|
'--output-format', 'parseable',
|
||||||
|
'--include-ids', 'yes'
|
||||||
|
]
|
||||||
|
|
||||||
|
stream = FilterStream(_term.terminfo())
|
||||||
|
|
||||||
|
old_stderr = _sys.stderr
|
||||||
|
try:
|
||||||
|
# pylint: disable = E1101
|
||||||
|
_sys.stderr = stream
|
||||||
|
from pylint import __pkginfo__
|
||||||
|
if __pkginfo__.numversion < (0, 13):
|
||||||
|
# The lint tool is not very user friendly, so we need a hack here.
|
||||||
|
lint.REPORTER_OPT_MAP['parseable'] = \
|
||||||
|
lambda: text.TextReporter2(stream)
|
||||||
|
reporter = text.TextReporter2(stream)
|
||||||
|
else:
|
||||||
|
reporter = text.ParseableTextReporter(stream)
|
||||||
|
lint.REPORTER_OPT_MAP['parseable'] = lambda: reporter
|
||||||
|
|
||||||
|
for path in args:
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
lint.Run(argv + [path], reporter=reporter)
|
||||||
|
except SystemExit:
|
||||||
|
pass # don't accept the exit. strange errors happen...
|
||||||
|
|
||||||
|
if stream.written:
|
||||||
|
print()
|
||||||
|
stream.written = False
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
_sys.stderr = old_stderr
|
||||||
|
|
||||||
|
return 0
|
31
_setup/py3/dev/analysis.py
Normal file
31
_setup/py3/dev/analysis.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2006, 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=================================
|
||||||
|
Support for code analysis tools
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Support for code analysis tools.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
|
||||||
|
def pylint(config, *args):
|
||||||
|
""" Run pylint """
|
||||||
|
from _setup.dev import _pylint
|
||||||
|
return _pylint.run(config, *args)
|
131
_setup/py3/dev/apidoc.py
Normal file
131
_setup/py3/dev/apidoc.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
==================
|
||||||
|
API doc builders
|
||||||
|
==================
|
||||||
|
|
||||||
|
API doc builders.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import os as _os
|
||||||
|
import re as _re
|
||||||
|
|
||||||
|
from _setup import shell as _shell
|
||||||
|
from _setup import term as _term
|
||||||
|
from _setup import util as _util
|
||||||
|
|
||||||
|
|
||||||
|
def _cleanup_epydoc(target):
|
||||||
|
"""
|
||||||
|
Cleanup epydoc generated files
|
||||||
|
|
||||||
|
This removes the epydoc-footer. It changes every release because of the
|
||||||
|
timestamp. That creates bad diffs (accidently it's also invalid html).
|
||||||
|
"""
|
||||||
|
search = _re.compile(r'<table[^<>]+width="100%%"').search
|
||||||
|
for filename in _shell.files(target, '*.html'):
|
||||||
|
fp = open(filename, 'r')
|
||||||
|
try:
|
||||||
|
html = fp.read()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
match = search(html)
|
||||||
|
if match:
|
||||||
|
start = match.start()
|
||||||
|
end = html.find('</table>', start)
|
||||||
|
if end >= 0:
|
||||||
|
end += len('</table>') + 1
|
||||||
|
html = html[:start] + html[end:]
|
||||||
|
fp = open(filename, 'w')
|
||||||
|
try:
|
||||||
|
fp.write(html)
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
|
||||||
|
_VERSION_SEARCH = _re.compile(
|
||||||
|
r'\bversion\s+(?P<major>\d+)\.(?P<minor>\d+)'
|
||||||
|
).search
|
||||||
|
def epydoc(**kwargs):
|
||||||
|
""" Run epydoc """
|
||||||
|
# pylint: disable = R0912
|
||||||
|
prog = kwargs.get('epydoc') or 'epydoc'
|
||||||
|
if not _os.path.dirname(_os.path.normpath(prog)):
|
||||||
|
prog = _shell.frompath(prog)
|
||||||
|
if not prog:
|
||||||
|
_term.red("%(epydoc)s not found",
|
||||||
|
epydoc=kwargs.get('epydoc') or 'epydoc',
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
version = _VERSION_SEARCH(_shell.spawn(prog, "--version", stdout=True))
|
||||||
|
if version is not None:
|
||||||
|
try:
|
||||||
|
version = tuple(map(int, version.group('major', 'minor')))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
version = None
|
||||||
|
if version is None:
|
||||||
|
_term.red("%(prog)s version not recognized" % locals())
|
||||||
|
return False
|
||||||
|
|
||||||
|
if version < (3, 0):
|
||||||
|
_term.red("%(prog)s is too old %(version)r < (3, 0)" % locals())
|
||||||
|
return False
|
||||||
|
|
||||||
|
env = dict(_os.environ)
|
||||||
|
|
||||||
|
prepend = kwargs.get('prepend')
|
||||||
|
if prepend:
|
||||||
|
toprepend = _os.pathsep.join(map(str, prepend))
|
||||||
|
if 'PYTHONPATH' in env:
|
||||||
|
env['PYTHONPATH'] = _os.pathsep.join((
|
||||||
|
toprepend, env['PYTHONPATH']
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
env['PYTHONPATH'] = toprepend
|
||||||
|
|
||||||
|
append = kwargs.get('append')
|
||||||
|
if append:
|
||||||
|
toappend = _os.pathsep.join(map(str, append))
|
||||||
|
if 'PYTHONPATH' in env:
|
||||||
|
env['PYTHONPATH'] = _os.pathsep.join((
|
||||||
|
env['PYTHONPATH'], toappend
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
env['PYTHONPATH'] = toappend
|
||||||
|
|
||||||
|
moreenv = kwargs.get('env')
|
||||||
|
if moreenv:
|
||||||
|
env.update(moreenv)
|
||||||
|
|
||||||
|
config = kwargs.get('config') or _shell.native('docs/epydoc.conf')
|
||||||
|
|
||||||
|
argv = [prog, '--config', config]
|
||||||
|
res = not _shell.spawn(*argv, **{'env': env})
|
||||||
|
if res:
|
||||||
|
cfg = _util.SafeConfigParser()
|
||||||
|
cfg.read(config)
|
||||||
|
try:
|
||||||
|
target = dict(cfg.items('epydoc'))['target']
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
_cleanup_epydoc(target)
|
||||||
|
return res
|
50
_setup/py3/dev/userdoc.py
Normal file
50
_setup/py3/dev/userdoc.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
===================
|
||||||
|
User doc builders
|
||||||
|
===================
|
||||||
|
|
||||||
|
User doc builders.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import os as _os
|
||||||
|
|
||||||
|
from _setup import shell as _shell
|
||||||
|
from _setup import term as _term
|
||||||
|
|
||||||
|
|
||||||
|
def sphinx(**kwargs):
|
||||||
|
""" Run sphinx """
|
||||||
|
prog = _shell.frompath('sphinx-build')
|
||||||
|
if prog is None:
|
||||||
|
_term.red("sphinx-build not found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
env = dict(_os.environ)
|
||||||
|
|
||||||
|
argv = [
|
||||||
|
prog, '-a',
|
||||||
|
'-d', _os.path.join(kwargs['build'], 'doctrees'),
|
||||||
|
'-b', 'html',
|
||||||
|
kwargs['source'],
|
||||||
|
kwargs['target'],
|
||||||
|
]
|
||||||
|
|
||||||
|
return not _shell.spawn(*argv, **{'env': env})
|
51
_setup/py3/dist.py
Normal file
51
_setup/py3/dist.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
================
|
||||||
|
dist utilities
|
||||||
|
================
|
||||||
|
|
||||||
|
dist utilities.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import shell as _shell
|
||||||
|
|
||||||
|
|
||||||
|
def run_setup(*args, **kwargs):
|
||||||
|
""" Run setup """
|
||||||
|
if 'setup' in kwargs:
|
||||||
|
script = kwargs.get('setup') or 'setup.py'
|
||||||
|
del kwargs['setup']
|
||||||
|
else:
|
||||||
|
script = 'setup.py'
|
||||||
|
if 'fakeroot' in kwargs:
|
||||||
|
fakeroot = kwargs['fakeroot']
|
||||||
|
del kwargs['fakeroot']
|
||||||
|
else:
|
||||||
|
fakeroot = None
|
||||||
|
if kwargs:
|
||||||
|
raise TypeError("Unrecognized keyword parameters")
|
||||||
|
|
||||||
|
script = _shell.native(script)
|
||||||
|
argv = [_sys.executable, script] + list(args)
|
||||||
|
if fakeroot:
|
||||||
|
argv.insert(0, fakeroot)
|
||||||
|
return not _shell.spawn(*argv)
|
253
_setup/py3/ext.py
Normal file
253
_setup/py3/ext.py
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
===================
|
||||||
|
C extension tools
|
||||||
|
===================
|
||||||
|
|
||||||
|
C extension tools.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
__test__ = False
|
||||||
|
|
||||||
|
from distutils import core as _core
|
||||||
|
from distutils import errors as _distutils_errors
|
||||||
|
from distutils import log
|
||||||
|
import os as _os
|
||||||
|
import posixpath as _posixpath
|
||||||
|
import shutil as _shutil
|
||||||
|
import tempfile as _tempfile
|
||||||
|
|
||||||
|
from _setup import commands as _commands
|
||||||
|
|
||||||
|
def _install_finalizer(installer):
|
||||||
|
if installer.without_c_extensions:
|
||||||
|
installer.distribution.ext_modules = []
|
||||||
|
|
||||||
|
def _build_finalizer(builder):
|
||||||
|
if builder.without_c_extensions:
|
||||||
|
builder.extensions = []
|
||||||
|
|
||||||
|
|
||||||
|
class Extension(_core.Extension):
|
||||||
|
"""
|
||||||
|
Extension with prerequisite check interface
|
||||||
|
|
||||||
|
If your check is cacheable (during the setup run), override
|
||||||
|
`cached_check_prerequisites`, `check_prerequisites` otherwise.
|
||||||
|
|
||||||
|
:IVariables:
|
||||||
|
`cached_check` : ``bool``
|
||||||
|
The cached check result
|
||||||
|
"""
|
||||||
|
cached_check = None
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
""" Initialization """
|
||||||
|
if 'depends' in kwargs:
|
||||||
|
self.depends = kwargs['depends'] or []
|
||||||
|
else:
|
||||||
|
self.depends = []
|
||||||
|
_core.Extension.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
# add include path
|
||||||
|
included = _posixpath.join('_setup', 'include')
|
||||||
|
if included not in self.include_dirs:
|
||||||
|
self.include_dirs.append(included)
|
||||||
|
|
||||||
|
# add cext.h to the dependencies
|
||||||
|
cext_h = _posixpath.join(included, 'cext.h')
|
||||||
|
if cext_h not in self.depends:
|
||||||
|
self.depends.append(cext_h)
|
||||||
|
|
||||||
|
_commands.add_option('install_lib', 'without-c-extensions',
|
||||||
|
help_text='Don\'t install C extensions',
|
||||||
|
inherit='install',
|
||||||
|
)
|
||||||
|
_commands.add_finalizer('install_lib', 'c-extensions',
|
||||||
|
_install_finalizer
|
||||||
|
)
|
||||||
|
_commands.add_option('build_ext', 'without-c-extensions',
|
||||||
|
help_text='Don\'t build C extensions',
|
||||||
|
inherit=('build', 'install_lib'),
|
||||||
|
)
|
||||||
|
_commands.add_finalizer('build_ext', 'c-extensions', _build_finalizer)
|
||||||
|
|
||||||
|
def check_prerequisites(self, build):
|
||||||
|
"""
|
||||||
|
Check prerequisites
|
||||||
|
|
||||||
|
The check should cover all dependencies needed for the extension to
|
||||||
|
be built and run. The method can do the following:
|
||||||
|
|
||||||
|
- return a false value: the extension will be built
|
||||||
|
- return a true value: the extension will be skipped. This is useful
|
||||||
|
for optional extensions
|
||||||
|
- raise an exception. This is useful for mandatory extensions
|
||||||
|
|
||||||
|
If the check result is cacheable (during the setup run), override
|
||||||
|
`cached_check_prerequisites` instead.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`build` : `BuildExt`
|
||||||
|
The extension builder
|
||||||
|
|
||||||
|
:Return: Skip the extension?
|
||||||
|
:Rtype: ``bool``
|
||||||
|
"""
|
||||||
|
if self.cached_check is None:
|
||||||
|
log.debug("PREREQ check for %s" % self.name)
|
||||||
|
self.cached_check = self.cached_check_prerequisites(build)
|
||||||
|
else:
|
||||||
|
log.debug("PREREQ check for %s (cached)" % self.name)
|
||||||
|
return self.cached_check
|
||||||
|
|
||||||
|
def cached_check_prerequisites(self, build):
|
||||||
|
"""
|
||||||
|
Check prerequisites
|
||||||
|
|
||||||
|
The check should cover all dependencies needed for the extension to
|
||||||
|
be built and run. The method can do the following:
|
||||||
|
|
||||||
|
- return a false value: the extension will be built
|
||||||
|
- return a true value: the extension will be skipped. This is useful
|
||||||
|
for optional extensions
|
||||||
|
- raise an exception. This is useful for mandatory extensions
|
||||||
|
|
||||||
|
If the check result is *not* cacheable (during the setup run),
|
||||||
|
override `check_prerequisites` instead.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`build` : `BuildExt`
|
||||||
|
The extension builder
|
||||||
|
|
||||||
|
:Return: Skip the extension?
|
||||||
|
:Rtype: ``bool``
|
||||||
|
"""
|
||||||
|
# pylint: disable = W0613
|
||||||
|
log.debug("Nothing to check for %s!" % self.name)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class ConfTest(object):
|
||||||
|
"""
|
||||||
|
Single conftest abstraction
|
||||||
|
|
||||||
|
:IVariables:
|
||||||
|
`_tempdir` : ``str``
|
||||||
|
The tempdir created for this test
|
||||||
|
|
||||||
|
`src` : ``str``
|
||||||
|
Name of the source file
|
||||||
|
|
||||||
|
`target` : ``str``
|
||||||
|
Target filename
|
||||||
|
|
||||||
|
`compiler` : ``CCompiler``
|
||||||
|
compiler instance
|
||||||
|
|
||||||
|
`obj` : ``list``
|
||||||
|
List of object filenames (``[str, ...]``)
|
||||||
|
"""
|
||||||
|
_tempdir = None
|
||||||
|
|
||||||
|
def __init__(self, build, source):
|
||||||
|
"""
|
||||||
|
Initialization
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`build` : ``distuils.command.build_ext.build_ext``
|
||||||
|
builder instance
|
||||||
|
|
||||||
|
`source` : ``str``
|
||||||
|
Source of the file to compile
|
||||||
|
"""
|
||||||
|
self._tempdir = tempdir = _tempfile.mkdtemp()
|
||||||
|
src = _os.path.join(tempdir, 'conftest.c')
|
||||||
|
fp = open(src, 'w')
|
||||||
|
try:
|
||||||
|
fp.write(source)
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
self.src = src
|
||||||
|
self.compiler = compiler = build.compiler
|
||||||
|
self.target = _os.path.join(tempdir, 'conftest')
|
||||||
|
self.obj = compiler.object_filenames([src], output_dir=tempdir)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
""" Destruction """
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
""" Destroy the conftest leftovers on disk """
|
||||||
|
tempdir, self._tempdir = self._tempdir, None
|
||||||
|
if tempdir is not None:
|
||||||
|
_shutil.rmtree(tempdir)
|
||||||
|
|
||||||
|
def compile(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Compile the conftest
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`kwargs` : ``dict``
|
||||||
|
Optional keyword parameters for the compiler call
|
||||||
|
|
||||||
|
:Return: Was the compilation successful?
|
||||||
|
:Rtype: ``bool``
|
||||||
|
"""
|
||||||
|
kwargs['output_dir'] = self._tempdir
|
||||||
|
try:
|
||||||
|
self.compiler.compile([self.src], **kwargs)
|
||||||
|
except _distutils_errors.CompileError:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def link(self, **kwargs):
|
||||||
|
r"""
|
||||||
|
Link the conftest
|
||||||
|
|
||||||
|
Before you can link the conftest objects they need to be `compile`\d.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`kwargs` : ``dict``
|
||||||
|
Optional keyword parameters for the linker call
|
||||||
|
|
||||||
|
:Return: Was the linking successful?
|
||||||
|
:Rtype: ``bool``
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.compiler.link_executable(self.obj, self.target, **kwargs)
|
||||||
|
except _distutils_errors.LinkError:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def pipe(self, mode="r"):
|
||||||
|
r"""
|
||||||
|
Execute the conftest binary and connect to it using a pipe
|
||||||
|
|
||||||
|
Before you can pipe to or from the conftest binary it needs to
|
||||||
|
be `link`\ed.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`mode` : ``str``
|
||||||
|
Pipe mode - r/w
|
||||||
|
|
||||||
|
:Return: The open pipe
|
||||||
|
:Rtype: ``file``
|
||||||
|
"""
|
||||||
|
return _os.popen(self.compiler.executable_filename(self.target), mode)
|
28
_setup/py3/make/__init__.py
Normal file
28
_setup/py3/make/__init__.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=====================
|
||||||
|
Package _setup.make
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Make tools, not distributed.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
# pylint: disable = W0611
|
||||||
|
from _setup.make._make import main, fail, warn, fatal, Target
|
338
_setup/py3/make/_make.py
Normal file
338
_setup/py3/make/_make.py
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
==================
|
||||||
|
Simple make base
|
||||||
|
==================
|
||||||
|
|
||||||
|
Simple make base.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import term as _term
|
||||||
|
|
||||||
|
|
||||||
|
class Failure(SystemExit):
|
||||||
|
""" Failure exception """
|
||||||
|
|
||||||
|
|
||||||
|
def fail(reason):
|
||||||
|
""" Fail for a reason """
|
||||||
|
raise Failure(reason)
|
||||||
|
|
||||||
|
|
||||||
|
def warn(message, name=None):
|
||||||
|
""" Warn """
|
||||||
|
_term.red("%(NAME)sWarning: %(msg)s",
|
||||||
|
NAME=name and "%s:" % name or '', msg=message
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def fatal(reason):
|
||||||
|
""" Fatal error, immediate stop """
|
||||||
|
print(reason, file=_sys.stderr)
|
||||||
|
_sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
class Target(object):
|
||||||
|
""" Target base class """
|
||||||
|
NAME = None
|
||||||
|
DEPS = None
|
||||||
|
HIDDEN = False
|
||||||
|
|
||||||
|
ERROR = None
|
||||||
|
|
||||||
|
def __init__(self, runner):
|
||||||
|
""" Base __init__ """
|
||||||
|
self.runner = runner
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
""" Default init hook """
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
""" Default run hook """
|
||||||
|
pass
|
||||||
|
|
||||||
|
def clean(self, scm=True, dist=False):
|
||||||
|
""" Default clean hook """
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class _Runner(object):
|
||||||
|
""" Runner """
|
||||||
|
|
||||||
|
def __init__(self, *targetscollection):
|
||||||
|
""" Initialization """
|
||||||
|
tdict = {}
|
||||||
|
if not targetscollection:
|
||||||
|
import __main__
|
||||||
|
targetscollection = [__main__]
|
||||||
|
|
||||||
|
from _setup.make import default_targets
|
||||||
|
if default_targets not in targetscollection:
|
||||||
|
targetscollection.append(default_targets)
|
||||||
|
|
||||||
|
for targets in targetscollection:
|
||||||
|
for value in list(vars(targets).values()):
|
||||||
|
if isinstance(value, type) and issubclass(value, Target) and \
|
||||||
|
value.NAME is not None:
|
||||||
|
if value.NAME in tdict:
|
||||||
|
if issubclass(value, tdict[value.NAME]):
|
||||||
|
pass # override base target
|
||||||
|
elif issubclass(tdict[value.NAME], value):
|
||||||
|
continue # found base later. ignore
|
||||||
|
else:
|
||||||
|
warn('Ambiguous target name', value.NAME)
|
||||||
|
continue
|
||||||
|
tdict[value.NAME] = value
|
||||||
|
self._tdict = tdict
|
||||||
|
self._itdict = {}
|
||||||
|
|
||||||
|
def print_help(self):
|
||||||
|
""" Print make help """
|
||||||
|
import textwrap as _textwrap
|
||||||
|
|
||||||
|
targets = self.targetinfo()
|
||||||
|
keys = []
|
||||||
|
for key, info in list(targets.items()):
|
||||||
|
if not info['hide']:
|
||||||
|
keys.append(key)
|
||||||
|
keys.sort()
|
||||||
|
length = max(list(map(len, keys)))
|
||||||
|
info = []
|
||||||
|
for key in keys:
|
||||||
|
info.append("%s%s" % (
|
||||||
|
(key + " " * length)[:length + 2],
|
||||||
|
_textwrap.fill(
|
||||||
|
targets[key]['desc'].strip(),
|
||||||
|
subsequent_indent=" " * (length + 2)
|
||||||
|
),
|
||||||
|
))
|
||||||
|
print("Available targets:\n\n" + "\n".join(info))
|
||||||
|
|
||||||
|
def targetinfo(self):
|
||||||
|
""" Extract target information """
|
||||||
|
result = {}
|
||||||
|
for name, cls in list(self._tdict.items()):
|
||||||
|
result[name] = {
|
||||||
|
'desc': cls.__doc__ or "no description",
|
||||||
|
'hide': cls.HIDDEN,
|
||||||
|
'deps': cls.DEPS or (),
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _topleveltargets(self):
|
||||||
|
""" Find all top level targets """
|
||||||
|
rev = {} # key is a dep of [values]
|
||||||
|
all_ = self.targetinfo()
|
||||||
|
for target, info in list(all_.items()):
|
||||||
|
for dep in info['deps']:
|
||||||
|
if dep not in all_:
|
||||||
|
fatal("Unknown target '%s' (dep of %s) -> exit" % (
|
||||||
|
dep, target
|
||||||
|
))
|
||||||
|
rev.setdefault(dep, []).append(target)
|
||||||
|
return [target for target, info in list(rev.items()) if not info]
|
||||||
|
|
||||||
|
def _run(self, target, seen=None):
|
||||||
|
""" Run a target """
|
||||||
|
if target.DEPS:
|
||||||
|
self(*target.DEPS, **{'seen': seen})
|
||||||
|
|
||||||
|
if not target.HIDDEN:
|
||||||
|
_term.yellow(">>> %(name)s", name=target.NAME)
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = target.run()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
result, target.ERROR = False, "^C -> exit"
|
||||||
|
except Failure as e:
|
||||||
|
result, target.ERROR = False, "%s: %s" % (target.NAME, e)
|
||||||
|
except (SystemExit, MemoryError):
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
target.ERROR = "%s errored:\n%s" % (target.NAME, ''.join(
|
||||||
|
traceback.format_exception(*_sys.exc_info())
|
||||||
|
))
|
||||||
|
result = False
|
||||||
|
else:
|
||||||
|
if result is None:
|
||||||
|
result = True
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _clean(self, target, scm, dist, seen=None):
|
||||||
|
""" Run a target """
|
||||||
|
if target.DEPS:
|
||||||
|
self.run_clean(
|
||||||
|
*target.DEPS, **{'scm': scm, 'dist': dist, 'seen': seen}
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = target.clean(scm, dist)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
result, target.ERROR = False, "^C -> exit"
|
||||||
|
except Failure as e:
|
||||||
|
result, target.ERROR = False, "%s: %s" % (target.NAME, e)
|
||||||
|
except (SystemExit, MemoryError):
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
target.ERROR = "%s errored:\n%s" % (target.NAME, ''.join(
|
||||||
|
traceback.format_exception(*_sys.exc_info())
|
||||||
|
))
|
||||||
|
result = False
|
||||||
|
else:
|
||||||
|
if result is None:
|
||||||
|
result = True
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _make_init(self, seen):
|
||||||
|
""" Make init mapper """
|
||||||
|
def init(target):
|
||||||
|
""" Return initialized target """
|
||||||
|
if target not in seen:
|
||||||
|
try:
|
||||||
|
seen[target] = self._tdict[target](self)
|
||||||
|
except KeyError:
|
||||||
|
fatal("Unknown target '%s' -> exit" % target)
|
||||||
|
else:
|
||||||
|
seen[target] = None
|
||||||
|
return seen[target]
|
||||||
|
return init
|
||||||
|
|
||||||
|
def run_clean(self, *targets, **kwargs):
|
||||||
|
""" Run targets """
|
||||||
|
def pop(name, default=None):
|
||||||
|
""" Pop """
|
||||||
|
if name in kwargs:
|
||||||
|
value = kwargs[name]
|
||||||
|
del kwargs[name]
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
seen = pop('seen', {})
|
||||||
|
scm = pop('scm', True)
|
||||||
|
dist = pop('dist', False)
|
||||||
|
if kwargs:
|
||||||
|
raise TypeError('Unknown keyword parameters')
|
||||||
|
|
||||||
|
if not targets:
|
||||||
|
top_targets = self._topleveltargets()
|
||||||
|
targets = self.targetinfo()
|
||||||
|
for item in top_targets:
|
||||||
|
del targets[item]
|
||||||
|
targets = list(targets.keys())
|
||||||
|
targets.sort()
|
||||||
|
top_targets.sort()
|
||||||
|
targets = top_targets + targets
|
||||||
|
|
||||||
|
init = self._make_init(seen)
|
||||||
|
for name in targets:
|
||||||
|
target = init(name)
|
||||||
|
if target is not None:
|
||||||
|
if not self._clean(target, scm=scm, dist=dist, seen=seen):
|
||||||
|
msg = target.ERROR
|
||||||
|
if msg is None:
|
||||||
|
msg = "Clean target %s returned error -> exit" % name
|
||||||
|
fatal(msg)
|
||||||
|
|
||||||
|
def __call__(self, *targets, **kwargs):
|
||||||
|
""" Run targets """
|
||||||
|
if 'seen' in kwargs:
|
||||||
|
seen = kwargs['seen']
|
||||||
|
del kwargs['seen']
|
||||||
|
else:
|
||||||
|
seen = None
|
||||||
|
if seen is None:
|
||||||
|
seen = self._itdict
|
||||||
|
if kwargs:
|
||||||
|
raise TypeError('Unknown keyword parameters')
|
||||||
|
|
||||||
|
init = self._make_init(seen)
|
||||||
|
for name in targets:
|
||||||
|
target = init(name)
|
||||||
|
if target is not None:
|
||||||
|
if not self._run(target, seen):
|
||||||
|
msg = target.ERROR
|
||||||
|
if msg is None:
|
||||||
|
msg = "Target %s returned error -> exit" % name
|
||||||
|
fatal(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def main(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
main(argv=None, *args, name=None)
|
||||||
|
|
||||||
|
Main start point. This function parses the command line and executes the
|
||||||
|
targets given through `argv`. If there are no targets given, a help output
|
||||||
|
is generated.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`argv` : sequence
|
||||||
|
Command line arguments. If omitted or ``None``, they are picked from
|
||||||
|
``sys.argv``.
|
||||||
|
|
||||||
|
`args` : ``tuple``
|
||||||
|
The list of modules with targets. If omitted, ``__main__``
|
||||||
|
is imported and treated as target module. Additionally the mechanism
|
||||||
|
always adds the `_setup.make` module (this one) to the list in order
|
||||||
|
to grab some default targets.
|
||||||
|
|
||||||
|
`name` : ``str``
|
||||||
|
Name of the executing module. If omitted or ``None``, ``'__main__'``
|
||||||
|
is assumed. If the final name is not ``'__main__'``, the function
|
||||||
|
returns immediately.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
name = kwargs['name']
|
||||||
|
except KeyError:
|
||||||
|
name = '__main__'
|
||||||
|
else:
|
||||||
|
del kwargs['name']
|
||||||
|
if name is None:
|
||||||
|
name = '__main__'
|
||||||
|
|
||||||
|
try:
|
||||||
|
argv = kwargs['argv']
|
||||||
|
except KeyError:
|
||||||
|
if not args:
|
||||||
|
args = (None,)
|
||||||
|
else:
|
||||||
|
del kwargs['argv']
|
||||||
|
args = (argv,) + args
|
||||||
|
|
||||||
|
if kwargs:
|
||||||
|
raise TypeError("Unrecognized keyword arguments for main()")
|
||||||
|
|
||||||
|
if name == '__main__':
|
||||||
|
argv, args = args[0], args[1:]
|
||||||
|
if argv is None:
|
||||||
|
argv = _sys.argv[1:]
|
||||||
|
|
||||||
|
runner = _Runner(*args)
|
||||||
|
if argv:
|
||||||
|
runner(*argv)
|
||||||
|
else:
|
||||||
|
runner.print_help()
|
110
_setup/py3/make/default_targets.py
Normal file
110
_setup/py3/make/default_targets.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
==================
|
||||||
|
Simple make base
|
||||||
|
==================
|
||||||
|
|
||||||
|
Simple make base.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import os as _os
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import make as _make
|
||||||
|
from _setup import shell as _shell
|
||||||
|
|
||||||
|
|
||||||
|
class MakefileTarget(_make.Target):
|
||||||
|
""" Create a make file """
|
||||||
|
NAME = 'makefile'
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
def escape(value):
|
||||||
|
""" Escape for make and shell """
|
||||||
|
return '"%s"' % value.replace(
|
||||||
|
'\\', '\\\\').replace(
|
||||||
|
'"', '\\"').replace(
|
||||||
|
'$', '\\$$')
|
||||||
|
def decorate(line, prefix='# ', width=78, char='~', padding=' '):
|
||||||
|
""" Decorate a line """
|
||||||
|
line = line.center(width - len(prefix))
|
||||||
|
return '%s%s%s%s%s%s' % (
|
||||||
|
prefix,
|
||||||
|
char * (len(line) - len(line.lstrip()) - len(padding)),
|
||||||
|
padding,
|
||||||
|
line.strip(),
|
||||||
|
padding,
|
||||||
|
char * (len(line) - len(line.rstrip()) - len(padding)),
|
||||||
|
)
|
||||||
|
|
||||||
|
python = escape(_sys.executable)
|
||||||
|
script = escape(_sys.argv[0])
|
||||||
|
targets = self.runner.targetinfo()
|
||||||
|
names = []
|
||||||
|
for name, info in list(targets.items()):
|
||||||
|
if not info['hide']:
|
||||||
|
names.append(name)
|
||||||
|
names.sort()
|
||||||
|
|
||||||
|
fp = open(_shell.native('Makefile'), 'w')
|
||||||
|
print(decorate("Generated Makefile, DO NOT EDIT"), file=fp)
|
||||||
|
print(decorate("python %s %s" % (
|
||||||
|
_os.path.basename(script), self.NAME
|
||||||
|
)), file=fp)
|
||||||
|
print(file=fp)
|
||||||
|
print("_default_:", file=fp)
|
||||||
|
print("\t@%s %s" % (python, script), file=fp)
|
||||||
|
for name in names:
|
||||||
|
print("\n", file=fp)
|
||||||
|
print("# %s" % \
|
||||||
|
targets[name]['desc'].splitlines()[0].strip(), file=fp)
|
||||||
|
print("%s:" % name, file=fp)
|
||||||
|
print("\t@%s %s %s" % (python, script, escape(name)), file=fp)
|
||||||
|
print(file=fp)
|
||||||
|
extension = self.extend(names)
|
||||||
|
if extension is not None:
|
||||||
|
print(extension, file=fp)
|
||||||
|
print(file=fp)
|
||||||
|
print(".PHONY: _default_ %s\n\n" % ' '.join(names), file=fp)
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
def extend(self, names):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CleanTarget(_make.Target):
|
||||||
|
""" Clean the mess """
|
||||||
|
NAME = 'clean'
|
||||||
|
_scm, _dist = True, False
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.runner.run_clean(scm=self._scm, dist=self._dist)
|
||||||
|
|
||||||
|
|
||||||
|
class DistCleanTarget(CleanTarget):
|
||||||
|
""" Clean as freshly unpacked dist package """
|
||||||
|
NAME = 'distclean'
|
||||||
|
_scm, _dist = False, True
|
||||||
|
|
||||||
|
|
||||||
|
class ExtraCleanTarget(CleanTarget):
|
||||||
|
""" Clean everything """
|
||||||
|
NAME = 'extraclean'
|
||||||
|
_scm, _dist = True, True
|
326
_setup/py3/make/targets.py
Normal file
326
_setup/py3/make/targets.py
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007 - 2013
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
==================
|
||||||
|
Standard targets
|
||||||
|
==================
|
||||||
|
|
||||||
|
Standard targets.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import os as _os
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import dist as _dist
|
||||||
|
from _setup import make as _make
|
||||||
|
from _setup import shell as _shell
|
||||||
|
from _setup import term as _term
|
||||||
|
|
||||||
|
|
||||||
|
class Distribution(_make.Target):
|
||||||
|
""" Build a distribution """
|
||||||
|
NAME = "dist"
|
||||||
|
DEPS = ["MANIFEST"]
|
||||||
|
|
||||||
|
_dist, _ebuilds, _changes = None, None, None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
exts = self.dist_pkg()
|
||||||
|
digests = self.digest_files(exts)
|
||||||
|
self.sign_digests(digests)
|
||||||
|
self.copy_ebuilds()
|
||||||
|
self.copy_changes()
|
||||||
|
|
||||||
|
def dist_pkg(self):
|
||||||
|
_term.green("Building package...")
|
||||||
|
_dist.run_setup("sdist", "--formats", "tar,zip",
|
||||||
|
fakeroot=_shell.frompath('fakeroot')
|
||||||
|
)
|
||||||
|
exts = ['.zip']
|
||||||
|
for name in _shell.files(self._dist, '*.tar', False):
|
||||||
|
exts.extend(self.compress(name))
|
||||||
|
_shell.rm(name)
|
||||||
|
return exts
|
||||||
|
|
||||||
|
def compress(self, filename):
|
||||||
|
""" Compress file """
|
||||||
|
ext = _os.path.splitext(filename)[1]
|
||||||
|
exts = []
|
||||||
|
exts.append('.'.join((ext, self.compress_gzip(filename))))
|
||||||
|
exts.append('.'.join((ext, self.compress_bzip2(filename))))
|
||||||
|
exts.append('.'.join((ext, self.compress_xz(filename))))
|
||||||
|
return exts
|
||||||
|
|
||||||
|
def compress_xz(self, filename):
|
||||||
|
outfilename = filename + '.xz'
|
||||||
|
self.compress_external(filename, outfilename, 'xz', '-c9')
|
||||||
|
return 'xz'
|
||||||
|
|
||||||
|
def compress_bzip2(self, filename):
|
||||||
|
outfilename = filename + '.bz2'
|
||||||
|
try:
|
||||||
|
import bz2 as _bz2
|
||||||
|
except ImportError:
|
||||||
|
self.compress_external(filename, outfilename, 'bzip2', '-c9')
|
||||||
|
else:
|
||||||
|
outfile = _bz2.BZ2File(outfilename, 'w')
|
||||||
|
self.compress_internal(filename, outfile, outfilename)
|
||||||
|
return 'bz2'
|
||||||
|
|
||||||
|
def compress_gzip(self, filename):
|
||||||
|
outfilename = filename + '.gz'
|
||||||
|
try:
|
||||||
|
import gzip as _gzip
|
||||||
|
except ImportError:
|
||||||
|
self.compress_external(filename, outfilename, 'gzip', '-c9')
|
||||||
|
else:
|
||||||
|
outfile = _gzip.GzipFile(filename, 'wb',
|
||||||
|
fileobj=open(outfilename, 'wb')
|
||||||
|
)
|
||||||
|
self.compress_internal(filename, outfile, outfilename)
|
||||||
|
return 'gz'
|
||||||
|
|
||||||
|
def compress_external(self, infile, outfile, *argv):
|
||||||
|
argv = list(argv)
|
||||||
|
argv[0] = _shell.frompath(argv[0])
|
||||||
|
if argv[0] is not None:
|
||||||
|
return not _shell.spawn(*argv, **{
|
||||||
|
'filepipe': True, 'stdin': infile, 'stdout': outfile,
|
||||||
|
})
|
||||||
|
return None
|
||||||
|
|
||||||
|
def compress_internal(self, filename, outfile, outfilename):
|
||||||
|
infile = open(filename, 'rb')
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
while 1:
|
||||||
|
chunk = infile.read(8192)
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
outfile.write(chunk)
|
||||||
|
outfile.close()
|
||||||
|
except:
|
||||||
|
e = _sys.exc_info()
|
||||||
|
try:
|
||||||
|
_shell.rm(outfilename)
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
raise e[0](e[1]).with_traceback(e[2])
|
||||||
|
finally:
|
||||||
|
del e
|
||||||
|
finally:
|
||||||
|
infile.close()
|
||||||
|
|
||||||
|
def digest_files(self, exts):
|
||||||
|
""" digest files """
|
||||||
|
digests = {}
|
||||||
|
digestnames = {}
|
||||||
|
for ext in exts:
|
||||||
|
for name in _shell.files(self._dist, '*' + ext, False):
|
||||||
|
basename = _os.path.basename(name)
|
||||||
|
if basename not in digests:
|
||||||
|
digests[basename] = []
|
||||||
|
digests[basename].extend(self.digest(name))
|
||||||
|
digestname = basename[:-len(ext)]
|
||||||
|
if digestname not in digestnames:
|
||||||
|
digestnames[digestname] = []
|
||||||
|
digestnames[digestname].append(basename)
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for name, basenames in digestnames.items():
|
||||||
|
result.append(_os.path.join(self._dist, name + '.digests'))
|
||||||
|
fp = open(result[-1], 'wb')
|
||||||
|
try:
|
||||||
|
fp.write(
|
||||||
|
b'\n# The file may contain MD5, SHA1 and SHA256 digests\n'
|
||||||
|
)
|
||||||
|
fp.write(b'# Check archive integrity with, e.g. md5sum -c\n')
|
||||||
|
fp.write(b'# Check digest file integrity with PGP\n\n')
|
||||||
|
basenames.sort()
|
||||||
|
for basename in basenames:
|
||||||
|
for digest in digests[basename]:
|
||||||
|
fp.write((
|
||||||
|
"%s *%s\n" % (digest, basename)).encode('utf-8')
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def digest(self, filename):
|
||||||
|
result = []
|
||||||
|
for method in (self.md5, self.sha1, self.sha256):
|
||||||
|
digest = method(filename)
|
||||||
|
if digest is not None:
|
||||||
|
result.append(digest)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def do_digest(self, hashfunc, name, filename):
|
||||||
|
filename = _shell.native(filename)
|
||||||
|
_term.green("%(digest)s-digesting %(name)s...",
|
||||||
|
digest=name, name=_os.path.basename(filename))
|
||||||
|
fp = open(filename, 'rb')
|
||||||
|
sig = hashfunc()
|
||||||
|
block = fp.read(8192)
|
||||||
|
while block:
|
||||||
|
sig.update(block)
|
||||||
|
block = fp.read(8192)
|
||||||
|
fp.close()
|
||||||
|
return sig.hexdigest()
|
||||||
|
|
||||||
|
param = {'sig': sig.hexdigest(), 'file': _os.path.basename(filename)}
|
||||||
|
fp = open("%s.%s" % (filename, name), "w")
|
||||||
|
fp.write("%(sig)s *%(file)s\n" % param)
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def md5(self, filename):
|
||||||
|
try:
|
||||||
|
from hashlib import md5
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
from md5 import new as md5
|
||||||
|
except ImportError:
|
||||||
|
_make.warn("md5 not found -> skip md5 digests", self.NAME)
|
||||||
|
return None
|
||||||
|
return self.do_digest(md5, "md5", filename)
|
||||||
|
|
||||||
|
def sha1(self, filename):
|
||||||
|
try:
|
||||||
|
from hashlib import sha1
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
from sha import new as sha1
|
||||||
|
except ImportError:
|
||||||
|
_make.warn("sha1 not found -> skip sha1 digests", self.NAME)
|
||||||
|
return None
|
||||||
|
return self.do_digest(sha1, "sha1", filename)
|
||||||
|
|
||||||
|
def sha256(self, filename):
|
||||||
|
try:
|
||||||
|
from hashlib import sha256
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
from Crypto.Hash.SHA256 import new as sha256
|
||||||
|
except ImportError:
|
||||||
|
_make.warn(
|
||||||
|
"sha256 not found -> skip sha256 digests", self.NAME
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
return self.do_digest(sha256, "sha256", filename)
|
||||||
|
|
||||||
|
def copy_ebuilds(self):
|
||||||
|
if self._ebuilds is not None:
|
||||||
|
for src in _shell.files(self._ebuilds, '*.ebuild'):
|
||||||
|
_shell.cp(src, self._dist)
|
||||||
|
|
||||||
|
def copy_changes(self):
|
||||||
|
if self._changes is not None:
|
||||||
|
_shell.cp(self._changes, self._dist)
|
||||||
|
|
||||||
|
def sign_digests(self, digests):
|
||||||
|
for digest in digests:
|
||||||
|
self.sign(digest, detach=False)
|
||||||
|
|
||||||
|
def sign(self, filename, detach=True):
|
||||||
|
filename = _shell.native(filename)
|
||||||
|
try:
|
||||||
|
from pyme import core, errors
|
||||||
|
from pyme.constants.sig import mode
|
||||||
|
except ImportError:
|
||||||
|
return self.sign_external(filename, detach=detach)
|
||||||
|
|
||||||
|
_term.green("signing %(name)s...", name=_os.path.basename(filename))
|
||||||
|
sigmode = [mode.CLEAR, mode.DETACH][bool(detach)]
|
||||||
|
fp = core.Data(file=filename)
|
||||||
|
sig = core.Data()
|
||||||
|
try:
|
||||||
|
c = core.Context()
|
||||||
|
except errors.GPGMEError:
|
||||||
|
return self.sign_external(filename, detach=detach)
|
||||||
|
c.set_armor(1)
|
||||||
|
try:
|
||||||
|
c.op_sign(fp, sig, sigmode)
|
||||||
|
except errors.GPGMEError as e:
|
||||||
|
_make.fail(str(e))
|
||||||
|
|
||||||
|
sig.seek(0, 0)
|
||||||
|
if detach:
|
||||||
|
open("%s.asc" % filename, "w").write(sig.read())
|
||||||
|
else:
|
||||||
|
open(filename, "w").write(sig.read())
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def sign_external(self, filename, detach=True):
|
||||||
|
""" Sign calling gpg """
|
||||||
|
gpg = _shell.frompath('gpg')
|
||||||
|
if gpg is None:
|
||||||
|
_make.warn('GPG not found -> cannot sign')
|
||||||
|
return False
|
||||||
|
if detach:
|
||||||
|
_shell.spawn(gpg,
|
||||||
|
'--armor',
|
||||||
|
'--output', filename + '.asc',
|
||||||
|
'--detach-sign',
|
||||||
|
'--',
|
||||||
|
filename,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_shell.spawn(gpg,
|
||||||
|
'--output', filename + '.signed',
|
||||||
|
'--clearsign',
|
||||||
|
'--',
|
||||||
|
filename,
|
||||||
|
)
|
||||||
|
_os.rename(filename + '.signed', filename)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def clean(self, scm, dist):
|
||||||
|
_term.green("Removing dist files...")
|
||||||
|
_shell.rm_rf(self._dist)
|
||||||
|
|
||||||
|
|
||||||
|
class Manifest(_make.Target):
|
||||||
|
""" Create manifest """
|
||||||
|
NAME = "MANIFEST"
|
||||||
|
HIDDEN = True
|
||||||
|
DEPS = ["doc"]
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
_term.green("Creating %(name)s...", name=self.NAME)
|
||||||
|
dest = _shell.native(self.NAME)
|
||||||
|
dest = open(dest, 'w')
|
||||||
|
for name in self.manifest_names():
|
||||||
|
dest.write("%s\n" % name)
|
||||||
|
dest.close()
|
||||||
|
|
||||||
|
def manifest_names(self):
|
||||||
|
import setup
|
||||||
|
for item in setup.manifest():
|
||||||
|
yield item
|
||||||
|
|
||||||
|
def clean(self, scm, dist):
|
||||||
|
""" Clean manifest """
|
||||||
|
if scm:
|
||||||
|
_term.green("Removing MANIFEST")
|
||||||
|
_shell.rm(self.NAME)
|
420
_setup/py3/setup.py
Normal file
420
_setup/py3/setup.py
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007 - 2013
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
===================
|
||||||
|
Main setup runner
|
||||||
|
===================
|
||||||
|
|
||||||
|
This module provides a wrapper around the distutils core setup.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import configparser as _config_parser
|
||||||
|
from distutils import core as _core
|
||||||
|
import os as _os
|
||||||
|
import posixpath as _posixpath
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import commands as _commands
|
||||||
|
from _setup import data as _data
|
||||||
|
from _setup import ext as _ext
|
||||||
|
from _setup import util as _util
|
||||||
|
from _setup import shell as _shell
|
||||||
|
|
||||||
|
|
||||||
|
def check_python_version(impl, version_min, version_max):
|
||||||
|
""" Check python version """
|
||||||
|
if impl == 'python':
|
||||||
|
version_info = _sys.version_info
|
||||||
|
elif impl == 'pypy':
|
||||||
|
version_info = getattr(_sys, 'pypy_version_info', None)
|
||||||
|
if not version_info:
|
||||||
|
return
|
||||||
|
elif impl == 'jython':
|
||||||
|
if not 'java' in _sys.platform.lower():
|
||||||
|
return
|
||||||
|
version_info = _sys.version_info
|
||||||
|
else:
|
||||||
|
raise AssertionError("impl not in ('python', 'pypy', 'jython')")
|
||||||
|
|
||||||
|
pyversion = list(map(int, version_info[:3]))
|
||||||
|
if version_min:
|
||||||
|
min_required = list(
|
||||||
|
map(int, '.'.join((version_min, '0.0.0')).split('.')[:3])
|
||||||
|
)
|
||||||
|
if pyversion < min_required:
|
||||||
|
raise EnvironmentError("Need at least %s %s (vs. %s)" % (
|
||||||
|
impl, version_min, '.'.join(map(str, pyversion))
|
||||||
|
))
|
||||||
|
if version_max:
|
||||||
|
max_required = list(map(int, version_max.split('.')))
|
||||||
|
max_required[-1] += 1
|
||||||
|
if pyversion >= max_required:
|
||||||
|
raise EnvironmentError("Need at max %s %s (vs. %s)" % (
|
||||||
|
impl,
|
||||||
|
version_max,
|
||||||
|
'.'.join(map(str, pyversion))
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def find_description(docs):
|
||||||
|
"""
|
||||||
|
Determine the package description from DESCRIPTION
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`docs` : ``dict``
|
||||||
|
Docs config section
|
||||||
|
|
||||||
|
:Return: Tuple of summary, description and license
|
||||||
|
(``('summary', 'description', 'license')``)
|
||||||
|
(all may be ``None``)
|
||||||
|
:Rtype: ``tuple``
|
||||||
|
"""
|
||||||
|
summary = None
|
||||||
|
filename = docs.get('meta.summary', 'SUMMARY').strip()
|
||||||
|
if filename and _os.path.isfile(filename):
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
summary = fp.read().strip().splitlines()[0].rstrip()
|
||||||
|
except IndexError:
|
||||||
|
summary = ''
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
description = None
|
||||||
|
filename = docs.get('meta.description', 'DESCRIPTION').strip()
|
||||||
|
if filename and _os.path.isfile(filename):
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
description = fp.read().rstrip()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
if summary is None and description:
|
||||||
|
from docutils import core
|
||||||
|
summary = core.publish_parts(
|
||||||
|
source=description,
|
||||||
|
source_path=filename,
|
||||||
|
writer_name='html',
|
||||||
|
)['title'].encode('utf-8')
|
||||||
|
|
||||||
|
return summary, description
|
||||||
|
|
||||||
|
|
||||||
|
def find_classifiers(docs):
|
||||||
|
"""
|
||||||
|
Determine classifiers from CLASSIFIERS
|
||||||
|
|
||||||
|
:return: List of classifiers (``['classifier', ...]``)
|
||||||
|
:rtype: ``list``
|
||||||
|
"""
|
||||||
|
filename = docs.get('meta.classifiers', 'CLASSIFIERS').strip()
|
||||||
|
if filename and _os.path.isfile(filename):
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
content = fp.read()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
content = [item.strip() for item in content.splitlines()]
|
||||||
|
return [item for item in content if item and not item.startswith('#')]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def find_provides(docs):
|
||||||
|
"""
|
||||||
|
Determine provides from PROVIDES
|
||||||
|
|
||||||
|
:return: List of provides (``['provides', ...]``)
|
||||||
|
:rtype: ``list``
|
||||||
|
"""
|
||||||
|
filename = docs.get('meta.provides', 'PROVIDES').strip()
|
||||||
|
if filename and _os.path.isfile(filename):
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
content = fp.read()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
content = [item.strip() for item in content.splitlines()]
|
||||||
|
return [item for item in content if item and not item.startswith('#')]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def find_license(docs):
|
||||||
|
"""
|
||||||
|
Determine license from LICENSE
|
||||||
|
|
||||||
|
:return: License text
|
||||||
|
:rtype: ``str``
|
||||||
|
"""
|
||||||
|
filename = docs.get('meta.license', 'LICENSE').strip()
|
||||||
|
if filename and _os.path.isfile(filename):
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
return fp.read().rstrip()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_packages(manifest):
|
||||||
|
""" Determine packages and subpackages """
|
||||||
|
packages = {}
|
||||||
|
collect = manifest.get('packages.collect', '').split()
|
||||||
|
lib = manifest.get('packages.lib', '.')
|
||||||
|
try:
|
||||||
|
sep = _os.path.sep
|
||||||
|
except AttributeError:
|
||||||
|
sep = _os.path.join('1', '2')[1:-1]
|
||||||
|
for root in collect:
|
||||||
|
for dirpath, _, filenames in _shell.walk(_os.path.join(lib, root)):
|
||||||
|
if dirpath.find('.svn') >= 0 or dirpath.find('.git') >= 0:
|
||||||
|
continue
|
||||||
|
if '__init__.py' in filenames:
|
||||||
|
packages[
|
||||||
|
_os.path.normpath(dirpath).replace(sep, '.')
|
||||||
|
] = None
|
||||||
|
packages = list(packages.keys())
|
||||||
|
packages.sort()
|
||||||
|
return packages
|
||||||
|
|
||||||
|
|
||||||
|
def find_data(name, docs):
|
||||||
|
""" Determine data files """
|
||||||
|
result = []
|
||||||
|
if docs.get('extra', '').strip():
|
||||||
|
result.append(_data.Documentation(docs['extra'].split(),
|
||||||
|
prefix='share/doc/%s' % name,
|
||||||
|
))
|
||||||
|
if docs.get('examples.dir', '').strip():
|
||||||
|
tpl = ['recursive-include %s *' % docs['examples.dir']]
|
||||||
|
if docs.get('examples.ignore', '').strip():
|
||||||
|
tpl.extend(["global-exclude %s" % item
|
||||||
|
for item in docs['examples.ignore'].split()
|
||||||
|
])
|
||||||
|
strip = int(docs.get('examples.strip', '') or 0)
|
||||||
|
result.append(_data.Documentation.from_templates(*tpl, **{
|
||||||
|
'strip': strip,
|
||||||
|
'prefix': 'share/doc/%s' % name,
|
||||||
|
'preserve': 1,
|
||||||
|
}))
|
||||||
|
if docs.get('userdoc.dir', '').strip():
|
||||||
|
tpl = ['recursive-include %s *' % docs['userdoc.dir']]
|
||||||
|
if docs.get('userdoc.ignore', '').strip():
|
||||||
|
tpl.extend(["global-exclude %s" % item
|
||||||
|
for item in docs['userdoc.ignore'].split()
|
||||||
|
])
|
||||||
|
strip = int(docs.get('userdoc.strip', '') or 0)
|
||||||
|
result.append(_data.Documentation.from_templates(*tpl, **{
|
||||||
|
'strip': strip,
|
||||||
|
'prefix': 'share/doc/%s' % name,
|
||||||
|
'preserve': 1,
|
||||||
|
}))
|
||||||
|
if docs.get('apidoc.dir', '').strip():
|
||||||
|
tpl = ['recursive-include %s *' % docs['apidoc.dir']]
|
||||||
|
if docs.get('apidoc.ignore', '').strip():
|
||||||
|
tpl.extend(["global-exclude %s" % item
|
||||||
|
for item in docs['apidoc.ignore'].split()
|
||||||
|
])
|
||||||
|
strip = int(docs.get('apidoc.strip', '') or 0)
|
||||||
|
result.append(_data.Documentation.from_templates(*tpl, **{
|
||||||
|
'strip': strip,
|
||||||
|
'prefix': 'share/doc/%s' % name,
|
||||||
|
'preserve': 1,
|
||||||
|
}))
|
||||||
|
if docs.get('man', '').strip():
|
||||||
|
result.extend(_data.Manpages.dispatch(docs['man'].split()))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def make_manifest(manifest, config, docs, kwargs):
|
||||||
|
""" Create file list to pack up """
|
||||||
|
# pylint: disable = R0912
|
||||||
|
kwargs = kwargs.copy()
|
||||||
|
kwargs['script_args'] = ['install']
|
||||||
|
kwargs['packages'] = list(kwargs.get('packages') or ()) + [
|
||||||
|
'_setup', '_setup.py2', '_setup.py3',
|
||||||
|
] + list(manifest.get('packages.extra', '').split() or ())
|
||||||
|
_core._setup_stop_after = "commandline"
|
||||||
|
try:
|
||||||
|
dist = _core.setup(**kwargs)
|
||||||
|
finally:
|
||||||
|
_core._setup_stop_after = None
|
||||||
|
|
||||||
|
result = ['MANIFEST', 'PKG-INFO', 'setup.py'] + list(config)
|
||||||
|
# TODO: work with default values:
|
||||||
|
for key in ('classifiers', 'description', 'summary', 'provides',
|
||||||
|
'license'):
|
||||||
|
filename = docs.get('meta.' + key, '').strip()
|
||||||
|
if filename and _os.path.isfile(filename):
|
||||||
|
result.append(filename)
|
||||||
|
|
||||||
|
cmd = dist.get_command_obj("build_py")
|
||||||
|
cmd.ensure_finalized()
|
||||||
|
#from pprint import pprint; pprint(("build_py", cmd.get_source_files()))
|
||||||
|
for item in cmd.get_source_files():
|
||||||
|
result.append(_posixpath.sep.join(
|
||||||
|
_os.path.normpath(item).split(_os.path.sep)
|
||||||
|
))
|
||||||
|
|
||||||
|
cmd = dist.get_command_obj("build_ext")
|
||||||
|
cmd.ensure_finalized()
|
||||||
|
#from pprint import pprint; pprint(("build_ext", cmd.get_source_files()))
|
||||||
|
for item in cmd.get_source_files():
|
||||||
|
result.append(_posixpath.sep.join(
|
||||||
|
_os.path.normpath(item).split(_os.path.sep)
|
||||||
|
))
|
||||||
|
for ext in cmd.extensions:
|
||||||
|
if ext.depends:
|
||||||
|
result.extend([_posixpath.sep.join(
|
||||||
|
_os.path.normpath(item).split(_os.path.sep)
|
||||||
|
) for item in ext.depends])
|
||||||
|
|
||||||
|
cmd = dist.get_command_obj("build_clib")
|
||||||
|
cmd.ensure_finalized()
|
||||||
|
if cmd.libraries:
|
||||||
|
#import pprint; pprint.pprint(("build_clib", cmd.get_source_files()))
|
||||||
|
for item in cmd.get_source_files():
|
||||||
|
result.append(_posixpath.sep.join(
|
||||||
|
_os.path.normpath(item).split(_os.path.sep)
|
||||||
|
))
|
||||||
|
for lib in cmd.libraries:
|
||||||
|
if lib[1].get('depends'):
|
||||||
|
result.extend([_posixpath.sep.join(
|
||||||
|
_os.path.normpath(item).split(_os.path.sep)
|
||||||
|
) for item in lib[1]['depends']])
|
||||||
|
|
||||||
|
cmd = dist.get_command_obj("build_scripts")
|
||||||
|
cmd.ensure_finalized()
|
||||||
|
#import pprint; pprint.pprint(("build_scripts", cmd.get_source_files()))
|
||||||
|
if cmd.get_source_files():
|
||||||
|
for item in cmd.get_source_files():
|
||||||
|
result.append(_posixpath.sep.join(
|
||||||
|
_os.path.normpath(item).split(_os.path.sep)
|
||||||
|
))
|
||||||
|
|
||||||
|
cmd = dist.get_command_obj("install_data")
|
||||||
|
cmd.ensure_finalized()
|
||||||
|
#from pprint import pprint; pprint(("install_data", cmd.get_inputs()))
|
||||||
|
try:
|
||||||
|
strings = str
|
||||||
|
except NameError:
|
||||||
|
strings = (str, str)
|
||||||
|
|
||||||
|
for item in cmd.get_inputs():
|
||||||
|
if isinstance(item, strings):
|
||||||
|
result.append(item)
|
||||||
|
else:
|
||||||
|
result.extend(item[1])
|
||||||
|
|
||||||
|
for item in manifest.get('dist', '').split():
|
||||||
|
result.append(item)
|
||||||
|
if _os.path.isdir(item):
|
||||||
|
for filename in _shell.files(item):
|
||||||
|
result.append(filename)
|
||||||
|
|
||||||
|
result = list(dict([(item, None) for item in result]).keys())
|
||||||
|
result.sort()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def run(config=('package.cfg',), ext=None, script_args=None, manifest_only=0):
|
||||||
|
""" Main runner """
|
||||||
|
if ext is None:
|
||||||
|
ext = []
|
||||||
|
|
||||||
|
cfg = _util.SafeConfigParser()
|
||||||
|
cfg.read(config)
|
||||||
|
pkg = dict(cfg.items('package'))
|
||||||
|
python_min = pkg.get('python.min') or None
|
||||||
|
python_max = pkg.get('python.max') or None
|
||||||
|
check_python_version('python', python_min, python_max)
|
||||||
|
pypy_min = pkg.get('pypy.min') or None
|
||||||
|
pypy_max = pkg.get('pypy.max') or None
|
||||||
|
check_python_version('pypy', pypy_min, pypy_max)
|
||||||
|
jython_min = pkg.get('jython.min') or None
|
||||||
|
jython_max = pkg.get('jython.max') or None
|
||||||
|
check_python_version('jython', jython_min, jython_max)
|
||||||
|
|
||||||
|
manifest = dict(cfg.items('manifest'))
|
||||||
|
try:
|
||||||
|
docs = dict(cfg.items('docs'))
|
||||||
|
except _config_parser.NoSectionError:
|
||||||
|
docs = {}
|
||||||
|
|
||||||
|
summary, description = find_description(docs)
|
||||||
|
scripts = manifest.get('scripts', '').strip() or None
|
||||||
|
if scripts:
|
||||||
|
scripts = scripts.split()
|
||||||
|
modules = manifest.get('modules', '').strip() or None
|
||||||
|
if modules:
|
||||||
|
modules = modules.split()
|
||||||
|
keywords = docs.get('meta.keywords', '').strip() or None
|
||||||
|
if keywords:
|
||||||
|
keywords = keywords.split()
|
||||||
|
revision = pkg.get('version.revision', '').strip()
|
||||||
|
if revision:
|
||||||
|
revision = "-r%s" % (revision,)
|
||||||
|
|
||||||
|
kwargs = {
|
||||||
|
'name': pkg['name'],
|
||||||
|
'version': "%s%s" % (
|
||||||
|
pkg['version.number'],
|
||||||
|
["", "-dev%s" % (revision,)][_util.humanbool(
|
||||||
|
'version.dev', pkg.get('version.dev', 'false')
|
||||||
|
)],
|
||||||
|
),
|
||||||
|
'provides': find_provides(docs),
|
||||||
|
'description': summary,
|
||||||
|
'long_description': description,
|
||||||
|
'classifiers': find_classifiers(docs),
|
||||||
|
'keywords': keywords,
|
||||||
|
'author': pkg['author.name'],
|
||||||
|
'author_email': pkg['author.email'],
|
||||||
|
'maintainer': pkg.get('maintainer.name'),
|
||||||
|
'maintainer_email': pkg.get('maintainer.email'),
|
||||||
|
'url': pkg.get('url.homepage'),
|
||||||
|
'download_url': pkg.get('url.download'),
|
||||||
|
'license': find_license(docs),
|
||||||
|
'package_dir': {'': manifest.get('packages.lib', '.')},
|
||||||
|
'packages': find_packages(manifest),
|
||||||
|
'py_modules': modules,
|
||||||
|
'ext_modules': ext,
|
||||||
|
'scripts': scripts,
|
||||||
|
'script_args': script_args,
|
||||||
|
'data_files': find_data(pkg['name'], docs),
|
||||||
|
'cmdclass': {
|
||||||
|
'build' : _commands.Build,
|
||||||
|
'build_ext' : _commands.BuildExt,
|
||||||
|
'install' : _commands.Install,
|
||||||
|
'install_data': _commands.InstallData,
|
||||||
|
'install_lib' : _commands.InstallLib,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for key in ('provides',):
|
||||||
|
if key not in _core.setup_keywords:
|
||||||
|
del kwargs[key]
|
||||||
|
|
||||||
|
if manifest_only:
|
||||||
|
return make_manifest(manifest, config, docs, kwargs)
|
||||||
|
|
||||||
|
# monkey-patch crappy manifest writer away.
|
||||||
|
from distutils.command import sdist
|
||||||
|
sdist.sdist.get_file_list = sdist.sdist.read_manifest
|
||||||
|
|
||||||
|
return _core.setup(**kwargs)
|
351
_setup/py3/shell.py
Normal file
351
_setup/py3/shell.py
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007 - 2013
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=================
|
||||||
|
Shell utilities
|
||||||
|
=================
|
||||||
|
|
||||||
|
Shell utilities.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import errno as _errno
|
||||||
|
import fnmatch as _fnmatch
|
||||||
|
import os as _os
|
||||||
|
import shutil as _shutil
|
||||||
|
import subprocess as _subprocess
|
||||||
|
import sys as _sys
|
||||||
|
import tempfile as _tempfile
|
||||||
|
|
||||||
|
cwd = _os.path.dirname(_os.path.abspath(_sys.argv[0]))
|
||||||
|
|
||||||
|
class ExitError(RuntimeError):
|
||||||
|
""" Exit error """
|
||||||
|
def __init__(self, code):
|
||||||
|
RuntimeError.__init__(self, code)
|
||||||
|
self.code = code
|
||||||
|
self.signal = None
|
||||||
|
|
||||||
|
|
||||||
|
class SignalError(ExitError):
|
||||||
|
""" Signal error """
|
||||||
|
def __init__(self, code, signal):
|
||||||
|
ExitError.__init__(self, code)
|
||||||
|
import signal as _signal
|
||||||
|
self.signal = signal
|
||||||
|
for key, val in vars(_signal).items():
|
||||||
|
if key.startswith('SIG') and not key.startswith('SIG_'):
|
||||||
|
if val == signal:
|
||||||
|
self.signalstr = key[3:]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.signalstr = '%04d' % signal
|
||||||
|
|
||||||
|
|
||||||
|
def native(path):
|
||||||
|
""" Convert slash path to native """
|
||||||
|
path = _os.path.sep.join(path.split('/'))
|
||||||
|
return _os.path.normpath(_os.path.join(cwd, path))
|
||||||
|
|
||||||
|
|
||||||
|
def cp(src, dest):
|
||||||
|
""" Copy src to dest """
|
||||||
|
_shutil.copy2(native(src), native(dest))
|
||||||
|
|
||||||
|
|
||||||
|
def cp_r(src, dest):
|
||||||
|
""" Copy -r src to dest """
|
||||||
|
_shutil.copytree(native(src), native(dest))
|
||||||
|
|
||||||
|
|
||||||
|
def rm(dest):
|
||||||
|
""" Remove a file """
|
||||||
|
try:
|
||||||
|
_os.unlink(native(dest))
|
||||||
|
except OSError as e:
|
||||||
|
if _errno.ENOENT != e.errno:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def rm_rf(dest):
|
||||||
|
""" Remove a tree """
|
||||||
|
dest = native(dest)
|
||||||
|
if _os.path.exists(dest):
|
||||||
|
for path in files(dest, '*'):
|
||||||
|
_os.chmod(native(path), 0o644)
|
||||||
|
_shutil.rmtree(dest)
|
||||||
|
|
||||||
|
|
||||||
|
mkstemp = _tempfile.mkstemp
|
||||||
|
|
||||||
|
|
||||||
|
def _pipespawn(argv, env):
|
||||||
|
""" Pipe spawn """
|
||||||
|
# pylint: disable = R0912
|
||||||
|
import pickle as _pickle
|
||||||
|
fd, name = mkstemp('.py')
|
||||||
|
try:
|
||||||
|
_os.write(fd, ((r"""
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
argv = pickle.loads(%(argv)s)
|
||||||
|
env = pickle.loads(%(env)s)
|
||||||
|
if 'X_JYTHON_WA_PATH' in env:
|
||||||
|
env['PATH'] = env['X_JYTHON_WA_PATH']
|
||||||
|
|
||||||
|
p = subprocess.Popen(argv, env=env)
|
||||||
|
result = p.wait()
|
||||||
|
if result < 0:
|
||||||
|
print("\n%%d 1" %% (-result))
|
||||||
|
sys.exit(2)
|
||||||
|
if result == 0:
|
||||||
|
sys.exit(0)
|
||||||
|
print("\n%%d" %% (result & 7,))
|
||||||
|
sys.exit(3)
|
||||||
|
""".strip() + "\n") % {
|
||||||
|
'argv': repr(_pickle.dumps(argv)),
|
||||||
|
'env': repr(_pickle.dumps(dict(env))),
|
||||||
|
}).encode('utf-8'))
|
||||||
|
fd, _ = None, _os.close(fd)
|
||||||
|
if _sys.platform == 'win32':
|
||||||
|
argv = []
|
||||||
|
for arg in [_sys.executable, name]:
|
||||||
|
if ' ' in arg or arg.startswith('"'):
|
||||||
|
arg = '"%s"' % arg.replace('"', '\\"')
|
||||||
|
argv.append(arg)
|
||||||
|
argv = ' '.join(argv)
|
||||||
|
shell = True
|
||||||
|
close_fds = False
|
||||||
|
else:
|
||||||
|
argv = [_sys.executable, name]
|
||||||
|
shell = False
|
||||||
|
close_fds = True
|
||||||
|
|
||||||
|
res = 0
|
||||||
|
if 'X_JYTHON_WA_PATH' in env:
|
||||||
|
env['PATH'] = env['X_JYTHON_WA_PATH']
|
||||||
|
|
||||||
|
proc = _subprocess.Popen(argv,
|
||||||
|
shell=shell,
|
||||||
|
stdin=_subprocess.PIPE,
|
||||||
|
stdout=_subprocess.PIPE,
|
||||||
|
close_fds=close_fds,
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
proc.stdin.close()
|
||||||
|
result = proc.stdout.read()
|
||||||
|
finally:
|
||||||
|
res = proc.wait()
|
||||||
|
if res != 0:
|
||||||
|
if res == 2:
|
||||||
|
signal, code = list(map(int, result.splitlines()[-1].split()))
|
||||||
|
raise SignalError(code, signal)
|
||||||
|
elif res == 3:
|
||||||
|
code = int(result.splitlines()[-1].strip())
|
||||||
|
raise ExitError(code)
|
||||||
|
raise ExitError(res)
|
||||||
|
|
||||||
|
return result.decode('latin-1')
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
if fd is not None:
|
||||||
|
_os.close(fd)
|
||||||
|
finally:
|
||||||
|
_os.unlink(name)
|
||||||
|
|
||||||
|
|
||||||
|
def _filepipespawn(infile, outfile, argv, env):
|
||||||
|
""" File Pipe spawn """
|
||||||
|
import pickle as _pickle
|
||||||
|
fd, name = mkstemp('.py')
|
||||||
|
try:
|
||||||
|
_os.write(fd, (("""
|
||||||
|
import os
|
||||||
|
import pickle
|
||||||
|
import sys
|
||||||
|
|
||||||
|
infile = pickle.loads(%(infile)s)
|
||||||
|
outfile = pickle.loads(%(outfile)s)
|
||||||
|
argv = pickle.loads(%(argv)s)
|
||||||
|
env = pickle.loads(%(env)s)
|
||||||
|
|
||||||
|
if infile is not None:
|
||||||
|
infile = open(infile, 'rb')
|
||||||
|
os.dup2(infile.fileno(), 0)
|
||||||
|
infile.close()
|
||||||
|
if outfile is not None:
|
||||||
|
outfile = open(outfile, 'wb')
|
||||||
|
os.dup2(outfile.fileno(), 1)
|
||||||
|
outfile.close()
|
||||||
|
|
||||||
|
pid = os.spawnve(os.P_NOWAIT, argv[0], argv, env)
|
||||||
|
result = os.waitpid(pid, 0)[1]
|
||||||
|
sys.exit(result & 7)
|
||||||
|
""".strip() + "\n") % {
|
||||||
|
'infile': repr(_pickle.dumps(_os.path.abspath(infile))),
|
||||||
|
'outfile': repr(_pickle.dumps(_os.path.abspath(outfile))),
|
||||||
|
'argv': repr(_pickle.dumps(argv)),
|
||||||
|
'env': repr(_pickle.dumps(env)),
|
||||||
|
}))
|
||||||
|
fd, _ = None, _os.close(fd)
|
||||||
|
if _sys.platform == 'win32':
|
||||||
|
argv = []
|
||||||
|
for arg in [_sys.executable, name]:
|
||||||
|
if ' ' in arg or arg.startswith('"'):
|
||||||
|
arg = '"%s"' % arg.replace('"', '\\"')
|
||||||
|
argv.append(arg)
|
||||||
|
argv = ' '.join(argv)
|
||||||
|
close_fds = False
|
||||||
|
shell = True
|
||||||
|
else:
|
||||||
|
argv = [_sys.executable, name]
|
||||||
|
close_fds = True
|
||||||
|
shell = False
|
||||||
|
|
||||||
|
p = _subprocess.Popen(
|
||||||
|
argv, env=env, shell=shell, close_fds=close_fds
|
||||||
|
)
|
||||||
|
return p.wait()
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
if fd is not None:
|
||||||
|
_os.close(fd)
|
||||||
|
finally:
|
||||||
|
_os.unlink(name)
|
||||||
|
|
||||||
|
|
||||||
|
def spawn(*argv, **kwargs):
|
||||||
|
""" Spawn a process """
|
||||||
|
if _sys.platform == 'win32':
|
||||||
|
newargv = []
|
||||||
|
for arg in argv:
|
||||||
|
if not arg or ' ' in arg or arg.startswith('"'):
|
||||||
|
arg = '"%s"' % arg.replace('"', '\\"')
|
||||||
|
newargv.append(arg)
|
||||||
|
argv = newargv
|
||||||
|
close_fds = False
|
||||||
|
shell = True
|
||||||
|
else:
|
||||||
|
close_fds = True
|
||||||
|
shell = False
|
||||||
|
|
||||||
|
env = kwargs.get('env')
|
||||||
|
if env is None:
|
||||||
|
env = dict(_os.environ)
|
||||||
|
if 'X_JYTHON_WA_PATH' in env:
|
||||||
|
env['PATH'] = env['X_JYTHON_WA_PATH']
|
||||||
|
|
||||||
|
echo = kwargs.get('echo')
|
||||||
|
if echo:
|
||||||
|
print(' '.join(argv))
|
||||||
|
filepipe = kwargs.get('filepipe')
|
||||||
|
if filepipe:
|
||||||
|
return _filepipespawn(
|
||||||
|
kwargs.get('stdin'), kwargs.get('stdout'), argv, env
|
||||||
|
)
|
||||||
|
pipe = kwargs.get('stdout')
|
||||||
|
if pipe:
|
||||||
|
return _pipespawn(argv, env)
|
||||||
|
|
||||||
|
p = _subprocess.Popen(argv, env=env, shell=shell, close_fds=close_fds)
|
||||||
|
return p.wait()
|
||||||
|
|
||||||
|
|
||||||
|
walk = _os.walk
|
||||||
|
|
||||||
|
|
||||||
|
def files(base, wildcard='[!.]*', recursive=1, prune=('.git', '.svn', 'CVS')):
|
||||||
|
""" Determine a filelist """
|
||||||
|
for dirpath, dirnames, filenames in walk(native(base)):
|
||||||
|
for item in prune:
|
||||||
|
if item in dirnames:
|
||||||
|
dirnames.remove(item)
|
||||||
|
|
||||||
|
filenames.sort()
|
||||||
|
for name in _fnmatch.filter(filenames, wildcard):
|
||||||
|
dest = _os.path.join(dirpath, name)
|
||||||
|
if dest.startswith(cwd):
|
||||||
|
dest = dest.replace(cwd, '', 1)
|
||||||
|
aslist = []
|
||||||
|
head, tail = _os.path.split(dest)
|
||||||
|
while tail:
|
||||||
|
aslist.append(tail)
|
||||||
|
head, tail = _os.path.split(head)
|
||||||
|
aslist.reverse()
|
||||||
|
dest = '/'.join(aslist)
|
||||||
|
yield dest
|
||||||
|
|
||||||
|
if not recursive:
|
||||||
|
break
|
||||||
|
dirnames.sort()
|
||||||
|
|
||||||
|
|
||||||
|
def dirs(base, wildcard='[!.]*', recursive=1, prune=('.git', '.svn', 'CVS')):
|
||||||
|
""" Determine a filelist """
|
||||||
|
for dirpath, dirnames, filenames in walk(native(base)):
|
||||||
|
for item in prune:
|
||||||
|
if item in dirnames:
|
||||||
|
dirnames.remove(item)
|
||||||
|
|
||||||
|
dirnames.sort()
|
||||||
|
for name in _fnmatch.filter(dirnames, wildcard):
|
||||||
|
dest = _os.path.join(dirpath, name)
|
||||||
|
if dest.startswith(cwd):
|
||||||
|
dest = dest.replace(cwd, '', 1)
|
||||||
|
aslist = []
|
||||||
|
head, tail = _os.path.split(dest)
|
||||||
|
while tail:
|
||||||
|
aslist.append(tail)
|
||||||
|
head, tail = _os.path.split(head)
|
||||||
|
aslist.reverse()
|
||||||
|
dest = '/'.join(aslist)
|
||||||
|
yield dest
|
||||||
|
|
||||||
|
if not recursive:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def frompath(executable):
|
||||||
|
""" Find executable in PATH """
|
||||||
|
# Based on distutils.spawn.find_executable.
|
||||||
|
path = _os.environ.get('PATH', '')
|
||||||
|
paths = [
|
||||||
|
_os.path.expanduser(item)
|
||||||
|
for item in path.split(_os.pathsep)
|
||||||
|
]
|
||||||
|
ext = _os.path.splitext(executable)[1]
|
||||||
|
exts = ['']
|
||||||
|
if _sys.platform == 'win32' or _os.name == 'os2':
|
||||||
|
eext = ['.exe', '.bat', '.py']
|
||||||
|
if ext not in eext:
|
||||||
|
exts.extend(eext)
|
||||||
|
|
||||||
|
for ext in exts:
|
||||||
|
if not _os.path.isfile(executable + ext):
|
||||||
|
for path in paths:
|
||||||
|
fname = _os.path.join(path, executable + ext)
|
||||||
|
if _os.path.isfile(fname):
|
||||||
|
# the file exists, we have a shot at spawn working
|
||||||
|
return fname
|
||||||
|
else:
|
||||||
|
return executable + ext
|
||||||
|
|
||||||
|
return None
|
28
_setup/py3/term/__init__.py
Normal file
28
_setup/py3/term/__init__.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=====================
|
||||||
|
Package _setup.term
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Terminal tools, not distributed.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
# pylint: disable = W0611
|
||||||
|
from _setup.term._term import terminfo, write, green, red, yellow, announce
|
116
_setup/py3/term/_term.py
Normal file
116
_setup/py3/term/_term.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=================
|
||||||
|
Terminal writer
|
||||||
|
=================
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
|
||||||
|
class _INFO(dict):
|
||||||
|
""" Terminal info dict """
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
""" Initialization """
|
||||||
|
dict.__init__(self, {
|
||||||
|
'NORMAL': '',
|
||||||
|
'BOLD': '',
|
||||||
|
'ERASE': '\n',
|
||||||
|
'RED': '',
|
||||||
|
'YELLOW': '',
|
||||||
|
'GREEN': '',
|
||||||
|
})
|
||||||
|
try:
|
||||||
|
import curses as _curses
|
||||||
|
except ImportError:
|
||||||
|
# fixup if a submodule of curses failed.
|
||||||
|
if 'curses' in _sys.modules:
|
||||||
|
del _sys.modules['curses']
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
_curses.setupterm()
|
||||||
|
except (TypeError, _curses.error):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
def make_color(color):
|
||||||
|
""" Make color control string """
|
||||||
|
seq = _curses.tigetstr('setaf').decode('ascii')
|
||||||
|
if seq is not None:
|
||||||
|
# XXX may fail - need better logic
|
||||||
|
seq = seq.replace("%p1", "") % color
|
||||||
|
return seq
|
||||||
|
|
||||||
|
self['NORMAL'] = _curses.tigetstr('sgr0').decode('ascii')
|
||||||
|
self['BOLD'] = _curses.tigetstr('bold').decode('ascii')
|
||||||
|
|
||||||
|
erase = _curses.tigetstr('el1').decode('ascii')
|
||||||
|
if erase is not None:
|
||||||
|
self['ERASE'] = erase + \
|
||||||
|
_curses.tigetstr('cr').decode('ascii')
|
||||||
|
|
||||||
|
self['RED'] = make_color(_curses.COLOR_RED)
|
||||||
|
self['YELLOW'] = make_color(_curses.COLOR_YELLOW)
|
||||||
|
self['GREEN'] = make_color(_curses.COLOR_GREEN)
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
""" Deliver always """
|
||||||
|
dict.get(self, key) or ""
|
||||||
|
|
||||||
|
|
||||||
|
def terminfo():
|
||||||
|
""" Get info singleton """
|
||||||
|
# pylint: disable = E1101, W0612
|
||||||
|
if terminfo.info is None:
|
||||||
|
terminfo.info = _INFO()
|
||||||
|
return terminfo.info
|
||||||
|
terminfo.info = None
|
||||||
|
|
||||||
|
|
||||||
|
def write(fmt, **kwargs):
|
||||||
|
""" Write stuff on the terminal """
|
||||||
|
parm = dict(terminfo())
|
||||||
|
parm.update(kwargs)
|
||||||
|
_sys.stdout.write(fmt % parm)
|
||||||
|
_sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def green(bmt, **kwargs):
|
||||||
|
""" Write something in green on screen """
|
||||||
|
announce("%%(GREEN)s%s%%(NORMAL)s" % bmt, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def red(bmt, **kwargs):
|
||||||
|
""" Write something in red on the screen """
|
||||||
|
announce("%%(BOLD)s%%(RED)s%s%%(NORMAL)s" % bmt, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def yellow(fmt, **kwargs):
|
||||||
|
""" Write something in yellow on the screen """
|
||||||
|
announce("%%(BOLD)s%%(YELLOW)s%s%%(NORMAL)s" % fmt, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def announce(fmt, **kwargs):
|
||||||
|
""" Announce something """
|
||||||
|
write(fmt, **kwargs)
|
||||||
|
_sys.stdout.write("\n")
|
||||||
|
_sys.stdout.flush()
|
||||||
|
|
||||||
|
|
63
_setup/py3/util.py
Normal file
63
_setup/py3/util.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2007, 2008, 2009, 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=================
|
||||||
|
Setup utilities
|
||||||
|
=================
|
||||||
|
|
||||||
|
Setup utilities.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
from distutils import util as _util
|
||||||
|
try:
|
||||||
|
from configparser import SafeConfigParser
|
||||||
|
except ImportError:
|
||||||
|
import configparser as _config_parser
|
||||||
|
class SafeConfigParser(_config_parser.ConfigParser):
|
||||||
|
""" Safe config parser """
|
||||||
|
def _interpolate(self, section, option, rawval, vars):
|
||||||
|
return rawval
|
||||||
|
|
||||||
|
def items(self, section):
|
||||||
|
return [(key, self.get(section, key))
|
||||||
|
for key in self.options(section)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def humanbool(name, value):
|
||||||
|
"""
|
||||||
|
Determine human boolean value
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`name` : ``str``
|
||||||
|
The config key (used for error message)
|
||||||
|
|
||||||
|
`value` : ``str``
|
||||||
|
The config value
|
||||||
|
|
||||||
|
:Return: The boolean value
|
||||||
|
:Rtype: ``bool``
|
||||||
|
|
||||||
|
:Exceptions:
|
||||||
|
- `ValueError` : The value could not be recognized
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return _util.strtobool(str(value).strip().lower() or 'no')
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError("Unrecognized config value: %s = %s" % (name, value))
|
158
bench.py
Executable file
158
bench.py
Executable file
@ -0,0 +1,158 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
=================================
|
||||||
|
Benchmark cssmin implementations
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Benchmark cssmin implementations.
|
||||||
|
|
||||||
|
Usage::
|
||||||
|
|
||||||
|
bench.py [-c COUNT] cssfile ...
|
||||||
|
|
||||||
|
-c COUNT number of runs per cssfile and minifier. Defaults to 10.
|
||||||
|
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__author__ = getattr(__author__, 'decode', lambda x: __author__)('latin-1')
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
__license__ = "Apache License, Version 2.0"
|
||||||
|
__version__ = "1.0.0"
|
||||||
|
|
||||||
|
import sys as _sys
|
||||||
|
import time as _time
|
||||||
|
|
||||||
|
class _p_02__rcssmin(object):
|
||||||
|
def __init__(self):
|
||||||
|
import rcssmin
|
||||||
|
cssmin = rcssmin._make_cssmin(python_only=True)
|
||||||
|
self.cssmin = lambda x: cssmin(x, keep_bang_comments=True)
|
||||||
|
|
||||||
|
class _p_03__rcssmin(object):
|
||||||
|
def __init__(self):
|
||||||
|
import _rcssmin
|
||||||
|
cssmin = _rcssmin.cssmin
|
||||||
|
self.cssmin = lambda x: cssmin(x, keep_bang_comments=True)
|
||||||
|
|
||||||
|
class cssmins(object):
|
||||||
|
from bench import cssmin as p_01_cssmin
|
||||||
|
p_02_rcssmin = _p_02__rcssmin()
|
||||||
|
try:
|
||||||
|
p_03__rcssmin = _p_03__rcssmin()
|
||||||
|
except ImportError:
|
||||||
|
print("C-Port not available")
|
||||||
|
|
||||||
|
print("Python Release: %s" % ".".join(map(str, _sys.version_info[:3])))
|
||||||
|
print("")
|
||||||
|
|
||||||
|
|
||||||
|
def slurp(filename):
|
||||||
|
""" Load a file """
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
return fp.read()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
|
||||||
|
def print_(*value, **kwargs):
|
||||||
|
""" Print stuff """
|
||||||
|
(kwargs.get('file') or _sys.stdout).write(
|
||||||
|
''.join(value) + kwargs.get('end', '\n')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def bench(filenames, count):
|
||||||
|
"""
|
||||||
|
Benchmark the minifiers with given css samples
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`filenames` : sequence
|
||||||
|
List of filenames
|
||||||
|
|
||||||
|
`count` : ``int``
|
||||||
|
Number of runs per css file and minifier
|
||||||
|
|
||||||
|
:Exceptions:
|
||||||
|
- `RuntimeError` : empty filenames sequence
|
||||||
|
"""
|
||||||
|
if not filenames:
|
||||||
|
raise RuntimeError("Missing files to benchmark")
|
||||||
|
try:
|
||||||
|
xrange
|
||||||
|
except NameError:
|
||||||
|
xrange = range
|
||||||
|
|
||||||
|
ports = [item for item in dir(cssmins) if item.startswith('p_')]
|
||||||
|
ports.sort()
|
||||||
|
ports = [(item[5:], getattr(cssmins, item).cssmin) for item in ports]
|
||||||
|
counted = [None for _ in xrange(count)]
|
||||||
|
flush = _sys.stdout.flush
|
||||||
|
|
||||||
|
inputs = [(filename, slurp(filename)) for filename in filenames]
|
||||||
|
for filename, script in inputs:
|
||||||
|
print_("Benchmarking %r..." % filename, end=" ")
|
||||||
|
flush()
|
||||||
|
outputs = [cssmin(script) for _, cssmin in ports]
|
||||||
|
failed = []
|
||||||
|
for idx in xrange(1, len(outputs)):
|
||||||
|
if outputs[idx] != outputs[0]:
|
||||||
|
failed.append(ports[idx][0])
|
||||||
|
print_("(%.1f KiB -> %.1f KiB)" % (
|
||||||
|
len(script) / 1024.0, len(outputs[0]) / 1024.0,
|
||||||
|
))
|
||||||
|
if failed:
|
||||||
|
for item in failed:
|
||||||
|
print_(" NOTE - Output of %r differs" % (item,))
|
||||||
|
else:
|
||||||
|
print_(" ok - Output identical")
|
||||||
|
flush()
|
||||||
|
times = []
|
||||||
|
for name, cssmin in ports:
|
||||||
|
print_(" Timing %s..." % name, end=" ")
|
||||||
|
flush()
|
||||||
|
start = _time.time()
|
||||||
|
for _ in counted:
|
||||||
|
cssmin(script)
|
||||||
|
end = _time.time()
|
||||||
|
times.append((end - start) * 1000 / count)
|
||||||
|
print_("%.2f ms" % times[-1], end=" ")
|
||||||
|
flush()
|
||||||
|
if len(times) <= 1:
|
||||||
|
print_()
|
||||||
|
else:
|
||||||
|
print_("(factor: %s)" % (', '.join([
|
||||||
|
'%.2f' % (timed / times[-1]) for timed in times[:-1]
|
||||||
|
])))
|
||||||
|
flush()
|
||||||
|
print_()
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
""" Main """
|
||||||
|
count, idx = 10, 0
|
||||||
|
if argv and argv[0] == '-c':
|
||||||
|
count, idx = int(argv[1]), 2
|
||||||
|
elif argv and argv[0].startswith('-c'):
|
||||||
|
count, idx = int(argv[0][2:]), 1
|
||||||
|
bench(argv[idx:], count)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main(_sys.argv[1:])
|
64
bench/LICENSE.cssmin
Normal file
64
bench/LICENSE.cssmin
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
`cssmin.py` - A Python port of the YUI CSS compressor.
|
||||||
|
|
||||||
|
Copyright (c) 2010 Zachary Voase
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
This software contains portions of the YUI CSS Compressor, notably some regular
|
||||||
|
expressions for reducing the size of CSS. The YUI Compressor source code can be
|
||||||
|
found at <http://github.com/yui/yuicompressor>, and is licensed as follows:
|
||||||
|
|
||||||
|
> YUI Compressor Copyright License Agreement (BSD License)
|
||||||
|
>
|
||||||
|
> Copyright (c) 2009, Yahoo! Inc.
|
||||||
|
> All rights reserved.
|
||||||
|
>
|
||||||
|
> Redistribution and use of this software in source and binary forms,
|
||||||
|
> with or without modification, are permitted provided that the following
|
||||||
|
> conditions are met:
|
||||||
|
>
|
||||||
|
> * Redistributions of source code must retain the above
|
||||||
|
> copyright notice, this list of conditions and the
|
||||||
|
> following disclaimer.
|
||||||
|
>
|
||||||
|
> * Redistributions in binary form must reproduce the above
|
||||||
|
> copyright notice, this list of conditions and the
|
||||||
|
> following disclaimer in the documentation and/or other
|
||||||
|
> materials provided with the distribution.
|
||||||
|
>
|
||||||
|
> * Neither the name of Yahoo! Inc. nor the names of its
|
||||||
|
> contributors may be used to endorse or promote products
|
||||||
|
> derived from this software without specific prior
|
||||||
|
> written permission of Yahoo! Inc.
|
||||||
|
>
|
||||||
|
> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
> AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
> IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
> DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
> FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
> DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
> SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
> OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
> OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
0
bench/__init__.py
Normal file
0
bench/__init__.py
Normal file
225
bench/cssmin.py
Normal file
225
bench/cssmin.py
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""`cssmin` - A Python port of the YUI CSS compressor."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
from StringIO import StringIO # The pure-Python StringIO supports unicode.
|
||||||
|
except ImportError:
|
||||||
|
from io import StringIO
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
__version__ = '0.2.0'
|
||||||
|
|
||||||
|
|
||||||
|
def remove_comments(css):
|
||||||
|
"""Remove all CSS comment blocks."""
|
||||||
|
|
||||||
|
iemac = False
|
||||||
|
preserve = False
|
||||||
|
comment_start = css.find("/*")
|
||||||
|
while comment_start >= 0:
|
||||||
|
# Preserve comments that look like `/*!...*/`.
|
||||||
|
# Slicing is used to make sure we don"t get an IndexError.
|
||||||
|
preserve = css[comment_start + 2:comment_start + 3] == "!"
|
||||||
|
|
||||||
|
comment_end = css.find("*/", comment_start + 2)
|
||||||
|
if comment_end < 0:
|
||||||
|
if not preserve:
|
||||||
|
css = css[:comment_start]
|
||||||
|
break
|
||||||
|
elif comment_end >= (comment_start + 2):
|
||||||
|
if css[comment_end - 1] == "\\":
|
||||||
|
# This is an IE Mac-specific comment; leave this one and the
|
||||||
|
# following one alone.
|
||||||
|
comment_start = comment_end + 2
|
||||||
|
iemac = True
|
||||||
|
elif iemac:
|
||||||
|
comment_start = comment_end + 2
|
||||||
|
iemac = False
|
||||||
|
elif not preserve:
|
||||||
|
css = css[:comment_start] + css[comment_end + 2:]
|
||||||
|
else:
|
||||||
|
comment_start = comment_end + 2
|
||||||
|
comment_start = css.find("/*", comment_start)
|
||||||
|
|
||||||
|
return css
|
||||||
|
|
||||||
|
|
||||||
|
def remove_unnecessary_whitespace(css):
|
||||||
|
"""Remove unnecessary whitespace characters."""
|
||||||
|
|
||||||
|
def pseudoclasscolon(css):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Prevents 'p :link' from becoming 'p:link'.
|
||||||
|
|
||||||
|
Translates 'p :link' into 'p ___PSEUDOCLASSCOLON___link'; this is
|
||||||
|
translated back again later.
|
||||||
|
"""
|
||||||
|
|
||||||
|
regex = re.compile(r"(^|\})(([^\{\:])+\:)+([^\{]*\{)")
|
||||||
|
match = regex.search(css)
|
||||||
|
while match:
|
||||||
|
css = ''.join([
|
||||||
|
css[:match.start()],
|
||||||
|
match.group().replace(":", "___PSEUDOCLASSCOLON___"),
|
||||||
|
css[match.end():]])
|
||||||
|
match = regex.search(css)
|
||||||
|
return css
|
||||||
|
|
||||||
|
css = pseudoclasscolon(css)
|
||||||
|
# Remove spaces from before things.
|
||||||
|
css = re.sub(r"\s+([!{};:>+\(\)\],])", r"\1", css)
|
||||||
|
|
||||||
|
# If there is a `@charset`, then only allow one, and move to the beginning.
|
||||||
|
css = re.sub(r"^(.*)(@charset \"[^\"]*\";)", r"\2\1", css)
|
||||||
|
css = re.sub(r"^(\s*@charset [^;]+;\s*)+", r"\1", css)
|
||||||
|
|
||||||
|
# Put the space back in for a few cases, such as `@media screen` and
|
||||||
|
# `(-webkit-min-device-pixel-ratio:0)`.
|
||||||
|
css = re.sub(r"\band\(", "and (", css)
|
||||||
|
|
||||||
|
# Put the colons back.
|
||||||
|
css = css.replace('___PSEUDOCLASSCOLON___', ':')
|
||||||
|
|
||||||
|
# Remove spaces from after things.
|
||||||
|
css = re.sub(r"([!{}:;>+\(\[,])\s+", r"\1", css)
|
||||||
|
|
||||||
|
return css
|
||||||
|
|
||||||
|
|
||||||
|
def remove_unnecessary_semicolons(css):
|
||||||
|
"""Remove unnecessary semicolons."""
|
||||||
|
|
||||||
|
return re.sub(r";+\}", "}", css)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_empty_rules(css):
|
||||||
|
"""Remove empty rules."""
|
||||||
|
|
||||||
|
return re.sub(r"[^\}\{]+\{\}", "", css)
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_rgb_colors_to_hex(css):
|
||||||
|
"""Convert `rgb(51,102,153)` to `#336699`."""
|
||||||
|
|
||||||
|
regex = re.compile(r"rgb\s*\(\s*([0-9,\s]+)\s*\)")
|
||||||
|
match = regex.search(css)
|
||||||
|
while match:
|
||||||
|
colors = map(lambda s: s.strip(), match.group(1).split(","))
|
||||||
|
hexcolor = '#%.2x%.2x%.2x' % tuple(map(int, colors))
|
||||||
|
css = css.replace(match.group(), hexcolor)
|
||||||
|
match = regex.search(css)
|
||||||
|
return css
|
||||||
|
|
||||||
|
|
||||||
|
def condense_zero_units(css):
|
||||||
|
"""Replace `0(px, em, %, etc)` with `0`."""
|
||||||
|
|
||||||
|
return re.sub(r"([\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", r"\1\2", css)
|
||||||
|
|
||||||
|
|
||||||
|
def condense_multidimensional_zeros(css):
|
||||||
|
"""Replace `:0 0 0 0;`, `:0 0 0;` etc. with `:0;`."""
|
||||||
|
|
||||||
|
css = css.replace(":0 0 0 0;", ":0;")
|
||||||
|
css = css.replace(":0 0 0;", ":0;")
|
||||||
|
css = css.replace(":0 0;", ":0;")
|
||||||
|
|
||||||
|
# Revert `background-position:0;` to the valid `background-position:0 0;`.
|
||||||
|
css = css.replace("background-position:0;", "background-position:0 0;")
|
||||||
|
|
||||||
|
return css
|
||||||
|
|
||||||
|
|
||||||
|
def condense_floating_points(css):
|
||||||
|
"""Replace `0.6` with `.6` where possible."""
|
||||||
|
|
||||||
|
return re.sub(r"(:|\s)0+\.(\d+)", r"\1.\2", css)
|
||||||
|
|
||||||
|
|
||||||
|
def condense_hex_colors(css):
|
||||||
|
"""Shorten colors from #AABBCC to #ABC where possible."""
|
||||||
|
|
||||||
|
regex = re.compile(r"([^\"'=\s])(\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])")
|
||||||
|
match = regex.search(css)
|
||||||
|
while match:
|
||||||
|
first = match.group(3) + match.group(5) + match.group(7)
|
||||||
|
second = match.group(4) + match.group(6) + match.group(8)
|
||||||
|
if first.lower() == second.lower():
|
||||||
|
css = css.replace(match.group(), match.group(1) + match.group(2) + '#' + first)
|
||||||
|
match = regex.search(css, match.end() - 3)
|
||||||
|
else:
|
||||||
|
match = regex.search(css, match.end())
|
||||||
|
return css
|
||||||
|
|
||||||
|
|
||||||
|
def condense_whitespace(css):
|
||||||
|
"""Condense multiple adjacent whitespace characters into one."""
|
||||||
|
|
||||||
|
return re.sub(r"\s+", " ", css)
|
||||||
|
|
||||||
|
|
||||||
|
def condense_semicolons(css):
|
||||||
|
"""Condense multiple adjacent semicolon characters into one."""
|
||||||
|
|
||||||
|
return re.sub(r";;+", ";", css)
|
||||||
|
|
||||||
|
|
||||||
|
def wrap_css_lines(css, line_length):
|
||||||
|
"""Wrap the lines of the given CSS to an approximate length."""
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
line_start = 0
|
||||||
|
for i, char in enumerate(css):
|
||||||
|
# It's safe to break after `}` characters.
|
||||||
|
if char == '}' and (i - line_start >= line_length):
|
||||||
|
lines.append(css[line_start:i + 1])
|
||||||
|
line_start = i + 1
|
||||||
|
|
||||||
|
if line_start < len(css):
|
||||||
|
lines.append(css[line_start:])
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def cssmin(css, wrap=None):
|
||||||
|
css = remove_comments(css)
|
||||||
|
css = condense_whitespace(css)
|
||||||
|
# A pseudo class for the Box Model Hack
|
||||||
|
# (see http://tantek.com/CSS/Examples/boxmodelhack.html)
|
||||||
|
css = css.replace('"\\"}\\""', "___PSEUDOCLASSBMH___")
|
||||||
|
css = remove_unnecessary_whitespace(css)
|
||||||
|
css = remove_unnecessary_semicolons(css)
|
||||||
|
css = condense_zero_units(css)
|
||||||
|
css = condense_multidimensional_zeros(css)
|
||||||
|
css = condense_floating_points(css)
|
||||||
|
css = normalize_rgb_colors_to_hex(css)
|
||||||
|
css = condense_hex_colors(css)
|
||||||
|
if wrap is not None:
|
||||||
|
css = wrap_css_lines(css, wrap)
|
||||||
|
css = css.replace("___PSEUDOCLASSBMH___", '"\\"}\\""')
|
||||||
|
css = condense_semicolons(css)
|
||||||
|
return css.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
import optparse
|
||||||
|
import sys
|
||||||
|
|
||||||
|
p = optparse.OptionParser(
|
||||||
|
prog="cssmin", version=__version__,
|
||||||
|
usage="%prog [--wrap N]",
|
||||||
|
description="""Reads raw CSS from stdin, and writes compressed CSS to stdout.""")
|
||||||
|
|
||||||
|
p.add_option(
|
||||||
|
'-w', '--wrap', type='int', default=None, metavar='N',
|
||||||
|
help="Wrap output to approximately N chars per line.")
|
||||||
|
|
||||||
|
options, args = p.parse_args()
|
||||||
|
sys.stdout.write(cssmin(sys.stdin.read(), wrap=options.wrap))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
3861
bench/wikipedia.css
Normal file
3861
bench/wikipedia.css
Normal file
File diff suppressed because it is too large
Load Diff
1
bench/wikipedia.min.css
vendored
Normal file
1
bench/wikipedia.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
5
compile-dev
Executable file
5
compile-dev
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# -Wwrite-strings
|
||||||
|
# -Wconversion
|
||||||
|
CFLAGS="-ggdb -std=c89 -pedantic -Wnested-externs -Wmissing-declarations -Wmissing-prototypes -Wcast-qual -Wcast-align -Wbad-function-cast -Wshadow -Wundef -Wdeclaration-after-statement -Wstrict-overflow=4 -Wmissing-include-dirs -Winit-self -Wextra -Wall -Werror -Wno-overlength-strings -Wno-long-long -Wno-unused-parameter -Wno-missing-field-initializers" exec make clean compile
|
17
docs/CHANGES
Normal file
17
docs/CHANGES
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
Changes with version 1.0.2
|
||||||
|
|
||||||
|
*) Added compat option to setup.py supporting the pip installer
|
||||||
|
|
||||||
|
*) Added support for pypy (1.9, 2.0)
|
||||||
|
|
||||||
|
*) Added support for jython (2.5)
|
||||||
|
|
||||||
|
|
||||||
|
Changes with version 1.0.1
|
||||||
|
|
||||||
|
*) Added support for Python 3.3
|
||||||
|
|
||||||
|
|
||||||
|
Changes with version 1.0.0
|
||||||
|
|
||||||
|
*) First stable release.
|
19
docs/CLASSIFIERS
Normal file
19
docs/CLASSIFIERS
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Development Status :: 5 - Production/Stable
|
||||||
|
Environment :: Web Environment
|
||||||
|
Intended Audience :: Developers
|
||||||
|
License :: OSI Approved
|
||||||
|
License :: OSI Approved :: Apache License, Version 2.0
|
||||||
|
Operating System :: OS Independent
|
||||||
|
Programming Language :: C
|
||||||
|
Programming Language :: Python
|
||||||
|
Programming Language :: Python :: 2
|
||||||
|
Programming Language :: Python :: 3
|
||||||
|
Programming Language :: Python :: Implementation :: CPython
|
||||||
|
Programming Language :: Python :: Implementation :: Jython
|
||||||
|
Programming Language :: Python :: Implementation :: PyPy
|
||||||
|
Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||||
|
Topic :: Software Development :: Libraries
|
||||||
|
Topic :: Software Development :: Libraries :: Python Modules
|
||||||
|
Topic :: Text Processing
|
||||||
|
Topic :: Text Processing :: Filters
|
||||||
|
Topic :: Utilities
|
84
docs/DESCRIPTION
Normal file
84
docs/DESCRIPTION
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
==============
|
||||||
|
CSS Minifier
|
||||||
|
==============
|
||||||
|
|
||||||
|
RCSSmin is a CSS minifier.
|
||||||
|
|
||||||
|
The minifier is based on the semantics of the `YUI compressor`_\, which itself
|
||||||
|
is based on `the rule list by Isaac Schlueter`_\.
|
||||||
|
|
||||||
|
This module is a re-implementation aiming for speed instead of maximum
|
||||||
|
compression, so it can be used at runtime (rather than during a preprocessing
|
||||||
|
step). RCSSmin does syntactical compression only (removing spaces, comments
|
||||||
|
and possibly semicolons). It does not provide semantic compression (like
|
||||||
|
removing empty blocks, collapsing redundant properties etc). It does, however,
|
||||||
|
support various CSS hacks (by keeping them working as intended).
|
||||||
|
|
||||||
|
Here's a feature list:
|
||||||
|
|
||||||
|
- Strings are kept, except that escaped newlines are stripped
|
||||||
|
- Space/Comments before the very end or before various characters are
|
||||||
|
stripped: ``:{});=>+],!`` (The colon (``:``) is a special case, a single
|
||||||
|
space is kept if it's outside a ruleset.)
|
||||||
|
- Space/Comments at the very beginning or after various characters are
|
||||||
|
stripped: ``{}(=:>+[,!``
|
||||||
|
- Optional space after unicode escapes is kept, resp. replaced by a simple
|
||||||
|
space
|
||||||
|
- whitespaces inside ``url()`` definitions are stripped
|
||||||
|
- Comments starting with an exclamation mark (``!``) can be kept optionally.
|
||||||
|
- All other comments and/or whitespace characters are replaced by a single
|
||||||
|
space.
|
||||||
|
- Multiple consecutive semicolons are reduced to one
|
||||||
|
- The last semicolon within a ruleset is stripped
|
||||||
|
- CSS Hacks supported:
|
||||||
|
|
||||||
|
- IE7 hack (``>/**/``)
|
||||||
|
- Mac-IE5 hack (``/*\*/.../**/``)
|
||||||
|
- The boxmodelhack is supported naturally because it relies on valid CSS2
|
||||||
|
strings
|
||||||
|
- Between ``:first-line`` and the following comma or curly brace a space is
|
||||||
|
inserted. (apparently it's needed for IE6)
|
||||||
|
- Same for ``:first-letter``
|
||||||
|
|
||||||
|
rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to
|
||||||
|
factor 50 or so (depending on the input).
|
||||||
|
|
||||||
|
Both python 2 (>= 2.4) and python 3 are supported.
|
||||||
|
|
||||||
|
.. _YUI compressor: https://github.com/yui/yuicompressor/
|
||||||
|
|
||||||
|
.. _the rule list by Isaac Schlueter: https://github.com/isaacs/cssmin/tree/
|
||||||
|
|
||||||
|
|
||||||
|
Copyright and License
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Copyright 2011 - 2014
|
||||||
|
André Malo or his licensors, as applicable.
|
||||||
|
|
||||||
|
The whole package (except for the files in the bench/ directory) is
|
||||||
|
distributed under the Apache License Version 2.0. You'll find a copy in the
|
||||||
|
root directory of the distribution or online at:
|
||||||
|
<http://www.apache.org/licenses/LICENSE-2.0>.
|
||||||
|
|
||||||
|
|
||||||
|
Bugs
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
No bugs, of course. ;-)
|
||||||
|
But if you've found one or have an idea how to improve rcssmin, feel free
|
||||||
|
to send a pull request on `github <https://github.com/ndparker/rcssmin>`_
|
||||||
|
or send a mail to <rcssmin-bugs@perlig.de>.
|
||||||
|
|
||||||
|
|
||||||
|
Author Information
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
André "nd" Malo <nd perlig.de>
|
||||||
|
GPG: 0x8103A37E
|
||||||
|
|
||||||
|
|
||||||
|
If God intended people to be naked, they would be born that way.
|
||||||
|
-- Oscar Wilde
|
||||||
|
|
||||||
|
.. vim:tw=72 syntax=rest
|
1
docs/PROVIDES
Normal file
1
docs/PROVIDES
Normal file
@ -0,0 +1 @@
|
|||||||
|
rcssmin (1.0)
|
1
docs/SUMMARY
Normal file
1
docs/SUMMARY
Normal file
@ -0,0 +1 @@
|
|||||||
|
CSS Minifier
|
5
docs/_userdoc/_static/ci.css
Normal file
5
docs/_userdoc/_static/ci.css
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.ci {
|
||||||
|
font-variant: small-caps;
|
||||||
|
font-family: serif;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
2
docs/_userdoc/_templates/layout.html
Normal file
2
docs/_userdoc/_templates/layout.html
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{% extends "default/layout.html" %}
|
||||||
|
{% set css_files = css_files + ['_static/ci.css'] %}
|
228
docs/_userdoc/conf.py
Normal file
228
docs/_userdoc/conf.py
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# This file is execfile()d with the current directory set to its containing dir.
|
||||||
|
#
|
||||||
|
# Note that not all possible configuration values are present in this
|
||||||
|
# autogenerated file.
|
||||||
|
#
|
||||||
|
# All configuration values have a default; values that are commented out
|
||||||
|
# serve to show the default.
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
sys.path.append(os.path.abspath('.'))
|
||||||
|
|
||||||
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
|
extensions = ['epydoc_sphinx']
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# The suffix of source filenames.
|
||||||
|
source_suffix = '.txt'
|
||||||
|
|
||||||
|
# The encoding of source files.
|
||||||
|
#source_encoding = 'utf-8'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = u'rCSSmin'
|
||||||
|
copyright = u'2014 Andr\xe9 Malo'
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
version = '1.0'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '1.0.2'
|
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
# for a list of supported languages.
|
||||||
|
#language = None
|
||||||
|
|
||||||
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
|
# non-false value, then it is used:
|
||||||
|
#today = ''
|
||||||
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
|
#today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
|
# List of documents that shouldn't be included in the build.
|
||||||
|
unused_docs = ['website_download']
|
||||||
|
|
||||||
|
# List of directories, relative to source directory, that shouldn't be searched
|
||||||
|
# for source files.
|
||||||
|
exclude_trees = ['_build']
|
||||||
|
|
||||||
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
|
#default_role = None
|
||||||
|
|
||||||
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
|
#add_function_parentheses = True
|
||||||
|
|
||||||
|
# If true, the current module name will be prepended to all description
|
||||||
|
# unit titles (such as .. function::).
|
||||||
|
#add_module_names = True
|
||||||
|
|
||||||
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
|
# output. They are ignored by default.
|
||||||
|
#show_authors = False
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# A list of ignored prefixes for module index sorting.
|
||||||
|
#modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
||||||
|
rst_prolog = """
|
||||||
|
.. role:: product(emphasis)
|
||||||
|
:class: ci
|
||||||
|
|
||||||
|
.. |rcssmin| replace:: :product:`rCSSmin`
|
||||||
|
|
||||||
|
.. role:: productb(strong)
|
||||||
|
:class: ci
|
||||||
|
|
||||||
|
.. |**rcssmin**| replace:: :productb:`rCSSmin`
|
||||||
|
"""
|
||||||
|
|
||||||
|
# -- Options for epydoc extension-----------------------------------------------
|
||||||
|
|
||||||
|
epydoc = dict(
|
||||||
|
rcssmin=os.path.join(os.path.abspath(os.path.pardir), 'apidoc'),
|
||||||
|
)
|
||||||
|
|
||||||
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||||
|
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||||
|
html_theme = 'default'
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
if html_theme == 'nature':
|
||||||
|
html_theme_options = {}
|
||||||
|
elif html_theme == 'agogo':
|
||||||
|
html_theme_options = dict(
|
||||||
|
pagewidth='100%',
|
||||||
|
documentwidth='80%',
|
||||||
|
sidebarwidth='20%',
|
||||||
|
)
|
||||||
|
elif html_theme == 'scrolls':
|
||||||
|
html_theme_options = {}
|
||||||
|
elif html_theme == 'haiku':
|
||||||
|
html_theme_options = {}
|
||||||
|
else:
|
||||||
|
html_theme_options = dict(
|
||||||
|
rightsidebar=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
html_sidebars = {
|
||||||
|
'**': ['localtoc.html', 'relations.html'],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
|
#html_theme_path = []
|
||||||
|
|
||||||
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
|
# "<project> v<release> documentation".
|
||||||
|
#html_title = None
|
||||||
|
|
||||||
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
|
#html_short_title = None
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
|
# of the sidebar.
|
||||||
|
#html_logo = None
|
||||||
|
|
||||||
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
|
# pixels large.
|
||||||
|
#html_favicon = None
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
|
# using the given strftime format.
|
||||||
|
#html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
|
# typographically correct entities.
|
||||||
|
#html_use_smartypants = True
|
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names.
|
||||||
|
#html_sidebars = {}
|
||||||
|
|
||||||
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
|
# template names.
|
||||||
|
#html_additional_pages = {}
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
html_use_modindex = False
|
||||||
|
|
||||||
|
# If false, no index is generated.
|
||||||
|
html_use_index = False
|
||||||
|
|
||||||
|
# If true, the index is split into individual pages for each letter.
|
||||||
|
#html_split_index = False
|
||||||
|
|
||||||
|
# If true, links to the reST sources are added to the pages.
|
||||||
|
html_show_sourcelink = False
|
||||||
|
|
||||||
|
# If true, an OpenSearch description file will be output, and all pages will
|
||||||
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
|
# base URL from which the finished HTML is served.
|
||||||
|
#html_use_opensearch = ''
|
||||||
|
|
||||||
|
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
|
#html_file_suffix = ''
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'rCSSmindoc'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
||||||
|
# The paper size ('letter' or 'a4').
|
||||||
|
#latex_paper_size = 'letter'
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#latex_font_size = '10pt'
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
|
latex_documents = [
|
||||||
|
('index', 'rcssmin.tex', u'rcssmin Documentation',
|
||||||
|
u'Andr\xe9 "nd" Malo', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
|
# the title page.
|
||||||
|
#latex_logo = None
|
||||||
|
|
||||||
|
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||||
|
# not chapters.
|
||||||
|
#latex_use_parts = False
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#latex_preamble = ''
|
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals.
|
||||||
|
#latex_appendices = []
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#latex_use_modindex = True
|
130
docs/_userdoc/epydoc_sphinx.py
Normal file
130
docs/_userdoc/epydoc_sphinx.py
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2010, 2011
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import posixpath
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
|
||||||
|
from sphinx.util import caption_ref_re
|
||||||
|
|
||||||
|
|
||||||
|
def relpath(path, start=os.path.curdir):
|
||||||
|
"""Return a relative version of a path - stolen from python2.6 """
|
||||||
|
|
||||||
|
if not path:
|
||||||
|
raise ValueError("no path specified")
|
||||||
|
|
||||||
|
start_list = os.path.abspath(start).split(sep)
|
||||||
|
path_list = os.path.abspath(path).split(sep)
|
||||||
|
|
||||||
|
# Work out how much of the filepath is shared by start and path.
|
||||||
|
i = len(os.path.commonprefix([start_list, path_list]))
|
||||||
|
|
||||||
|
rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
|
||||||
|
if not rel_list:
|
||||||
|
return os.path.curdir
|
||||||
|
return os.path.join(*rel_list)
|
||||||
|
|
||||||
|
try:
|
||||||
|
relpath = os.path.relpath
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def make_roles(app):
|
||||||
|
""" Make roles """
|
||||||
|
epydoc = app.config.epydoc
|
||||||
|
if epydoc is not None:
|
||||||
|
for name, basedir in epydoc.iteritems():
|
||||||
|
app.add_role(name, make_epydoc_role(app, basedir))
|
||||||
|
|
||||||
|
|
||||||
|
def make_epydoc_role(app, epydoc):
|
||||||
|
""" Make a single role """
|
||||||
|
try:
|
||||||
|
fp = open(os.path.join(epydoc, 'api-objects.txt'))
|
||||||
|
try:
|
||||||
|
apis = dict([
|
||||||
|
line.strip().split(None, 1) for line in fp if line.strip()
|
||||||
|
])
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
except IOError:
|
||||||
|
app.warn("Epydoc description at %s not found" % (epydoc,))
|
||||||
|
apis = {}
|
||||||
|
|
||||||
|
def epydoc_role(role, rawtext, text, lineno, inliner, options={},
|
||||||
|
content=[]):
|
||||||
|
""" Actual role callback """
|
||||||
|
match = caption_ref_re.match(text)
|
||||||
|
if match:
|
||||||
|
extra, (text, ref) = True, match.group(1, 2)
|
||||||
|
text = text.strip()
|
||||||
|
if text.startswith('|') and text.endswith('|'):
|
||||||
|
text = text[1:-1]
|
||||||
|
extra = False
|
||||||
|
else:
|
||||||
|
extra, text, ref = False, None, text
|
||||||
|
if ref.endswith('()'):
|
||||||
|
ref = ref[:-2].strip()
|
||||||
|
parens = text is None
|
||||||
|
else:
|
||||||
|
parens = False
|
||||||
|
|
||||||
|
if '/' in ref:
|
||||||
|
chunks = ref.split('/', 1)
|
||||||
|
if not chunks[0]: # Main page
|
||||||
|
uri = 'index.html'
|
||||||
|
else:
|
||||||
|
uri = apis.get(''.join(chunks))
|
||||||
|
if text is None:
|
||||||
|
text = chunks[1]
|
||||||
|
else:
|
||||||
|
uri = apis.get(ref)
|
||||||
|
if not text:
|
||||||
|
text = ref
|
||||||
|
if parens:
|
||||||
|
text += '()'
|
||||||
|
|
||||||
|
if uri is None:
|
||||||
|
node = nodes.literal(rawtext, text)
|
||||||
|
else:
|
||||||
|
baseuri = relpath(
|
||||||
|
epydoc,
|
||||||
|
os.path.dirname(inliner.document.current_source)
|
||||||
|
).split(os.path.sep)
|
||||||
|
for idx, elem in enumerate(baseuri):
|
||||||
|
if elem == os.path.curdir:
|
||||||
|
baseuri[idx] = '.'
|
||||||
|
elif elem == os.path.pardir:
|
||||||
|
baseuri[idx] = '..'
|
||||||
|
baseuri = '/'.join(baseuri)
|
||||||
|
uri = posixpath.join(baseuri, uri)
|
||||||
|
if not extra:
|
||||||
|
text =u'\u2192\xa0' + text
|
||||||
|
node = nodes.reference(rawtext, text, refuri=uri, **options)
|
||||||
|
if not extra:
|
||||||
|
node = nodes.literal(rawtext, '', node)
|
||||||
|
return [node], []
|
||||||
|
|
||||||
|
return epydoc_role
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
app.add_config_value('epydoc', None, 'html')
|
||||||
|
app.connect('builder-inited', make_roles)
|
167
docs/_userdoc/index.txt
Normal file
167
docs/_userdoc/index.txt
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
.. license:
|
||||||
|
Copyright 2011 - 2014
|
||||||
|
André Malo or his licensors, as applicable
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
========================
|
||||||
|
rCSSmin - CSS Minifier
|
||||||
|
========================
|
||||||
|
|
||||||
|
|**rcssmin**| is a CSS minifier written in python.
|
||||||
|
|
||||||
|
The minifier is based on the semantics of the `YUI compressor`_\, which itself
|
||||||
|
is based on `the rule list by Isaac Schlueter`_\.
|
||||||
|
|
||||||
|
This module is a re-implementation aiming for speed instead of maximum
|
||||||
|
compression, so it can be used at runtime (rather than during a preprocessing
|
||||||
|
step). |rcssmin| does syntactical compression only (removing spaces, comments
|
||||||
|
and possibly semicolons). It does not provide semantic compression (like
|
||||||
|
removing empty blocks, collapsing redundant properties etc). It does, however,
|
||||||
|
support various CSS hacks (by keeping them working as intended).
|
||||||
|
|
||||||
|
Here's a feature list:
|
||||||
|
|
||||||
|
- Strings are kept, except that escaped newlines are stripped
|
||||||
|
- Space/Comments before the very end or before various characters are
|
||||||
|
stripped: ``:{});=>+],!`` (The colon (``:``) is a special case, a single
|
||||||
|
space is kept if it's outside a ruleset.)
|
||||||
|
- Space/Comments at the very beginning or after various characters are
|
||||||
|
stripped: ``{}(=:>+[,!``
|
||||||
|
- Optional space after unicode escapes is kept, resp. replaced by a simple
|
||||||
|
space
|
||||||
|
- whitespaces inside ``url()`` definitions are stripped
|
||||||
|
- Comments starting with an exclamation mark (``!``) can be kept optionally.
|
||||||
|
- All other comments and/or whitespace characters are replaced by a single
|
||||||
|
space.
|
||||||
|
- Multiple consecutive semicolons are reduced to one
|
||||||
|
- The last semicolon within a ruleset is stripped
|
||||||
|
- CSS Hacks supported:
|
||||||
|
|
||||||
|
- IE7 hack (``>/**/``)
|
||||||
|
- Mac-IE5 hack (``/*\*/.../**/``)
|
||||||
|
- The boxmodelhack is supported naturally because it relies on valid CSS2
|
||||||
|
strings
|
||||||
|
- Between ``:first-line`` and the following comma or curly brace a space is
|
||||||
|
inserted. (apparently it's needed for IE6)
|
||||||
|
- Same for ``:first-letter``
|
||||||
|
|
||||||
|
rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to
|
||||||
|
factor 50 or so (depending on the input).
|
||||||
|
|
||||||
|
.. _YUI compressor: https://github.com/yui/yuicompressor/
|
||||||
|
|
||||||
|
.. _the rule list by Isaac Schlueter: https://github.com/isaacs/cssmin/tree/
|
||||||
|
|
||||||
|
|rcssmin| works with both python 2 (starting with python 2.4) and python 3.
|
||||||
|
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
A :rcssmin:`generated API documentation </>` is available. But you can
|
||||||
|
just look into the module. It provides a simple function, called
|
||||||
|
``cssmin`` which takes the css as a string and returns the minified
|
||||||
|
css as a string.
|
||||||
|
|
||||||
|
The module additionally provides a "streamy" interface:
|
||||||
|
|
||||||
|
.. sourcecode:: console
|
||||||
|
|
||||||
|
$ python -mrcssmin <css >minified
|
||||||
|
|
||||||
|
It takes two options:
|
||||||
|
|
||||||
|
-b Keep bang-comments (Comments starting with an exclamation mark)
|
||||||
|
-p Use the python implementation (not the C implementation)
|
||||||
|
|
||||||
|
|
||||||
|
Development Status
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|rcssmin| is stable.
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
|rcssmin| is available under the terms and conditions of the "Apache License,
|
||||||
|
Version 2.0." You'll find the detailed licensing terms in the root
|
||||||
|
directory of the source distribution package or online at
|
||||||
|
`http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
<http://www.apache.org/licenses/LICENSE-2.0>`_.
|
||||||
|
|
||||||
|
|
||||||
|
.. placeholder: Download
|
||||||
|
|
||||||
|
|
||||||
|
Bugs
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
No bugs, of course. ;-)
|
||||||
|
But if you've found one or have an idea how to improve |rcssmin|, feel free
|
||||||
|
to send a pull request on `github <https://github.com/ndparker/rcssmin>`_
|
||||||
|
or send a mail to <rcssmin-bugs@perlig.de>.
|
||||||
|
|
||||||
|
|
||||||
|
Author Information
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|rcssmin| was written and is maintained by André Malo.
|
||||||
|
|
||||||
|
|
||||||
|
Trivia / Fun
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|rCSSmin| analyzes the CSS input using a simple regular expression, which
|
||||||
|
looks like this:
|
||||||
|
|
||||||
|
.. sourcecode:: text
|
||||||
|
|
||||||
|
pattern = (
|
||||||
|
r'([^\\"\047u>@\r\n\f\040\t/;:{}]+)|(?<=[{}(=:>+[,!])((?:[\r\n\f\040'
|
||||||
|
r'\t]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+)|^((?:[\r\n\f\040\t]|(?:/'
|
||||||
|
r'\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+)|((?:[\r\n\f\040\t]|(?:/\*[^*]*\*'
|
||||||
|
r'+(?:[^/*][^*]*\*+)*/))+)(?=(([:{});=>+\],!])|$)?)|;((?:[\r\n\f\040'
|
||||||
|
r'\t]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*(?:;(?:[\r\n\f\040\t]|(?:/'
|
||||||
|
r'\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*)(?=(\})?)|(\{)|(\})|((?:(?:\047'
|
||||||
|
r'[^\047\\\r\n\f]*(?:\\[^\r\n\f][^\047\\\r\n\f]*)*\047)|(?:"[^"\\\r\n'
|
||||||
|
r'\f]*(?:\\[^\r\n\f][^"\\\r\n\f]*)*")))|(?<![^\000-\054\056\057\072-'
|
||||||
|
r'\100\133-\136\140\173-\177])url\([\r\n\f\040\t]*((?:(?:\047[^\047'
|
||||||
|
r'\\]*(?:\\(?:[^\r]|\r\n?)[^\047\\]*)*\047)|(?:"[^"\\]*(?:\\(?:[^\r]'
|
||||||
|
r'|\r\n?)[^"\\]*)*"))|(?:(?:[^\000-\040"\047()\\\177]*(?:(?:\\(?:[0-'
|
||||||
|
r'9a-fA-F]{1,6}(?:[\040\n\t\f]|\r\n?)?|[^\n\r\f0-9a-fA-F]))[^\000-\0'
|
||||||
|
r'40"\047()\\\177]*)*)(?:(?:[\r\n\f\040\t]+|(?:\\(?:[\n\f]|\r\n?))+)'
|
||||||
|
r'(?:(?:[^\000-\040"\047()\\\177]|(?:\\(?:[0-9a-fA-F]{1,6}(?:[\040\n'
|
||||||
|
r'\t\f]|\r\n?)?|[^\n\r\f0-9a-fA-F]))|(?:\\(?:[\n\f]|\r\n?)))[^\000-\0'
|
||||||
|
r'40"\047()\\\177]*(?:(?:\\(?:[0-9a-fA-F]{1,6}(?:[\040\n\t\f]|\r\n?)'
|
||||||
|
r'?|[^\n\r\f0-9a-fA-F]))[^\000-\040"\047()\\\177]*)*)+)*))[\r\n\f\04'
|
||||||
|
r'0\t]*\)|(@[mM][eE][dD][iI][aA])(?![^\000-\054\056\057\072-\100\133'
|
||||||
|
r'-\136\140\173-\177])|((?:>/\*\*/))((?:[\r\n\f\040\t]|(?:/\*[^*]*\*'
|
||||||
|
r'+(?:[^/*][^*]*\*+)*/))*)|(:[fF][iI][rR][sS][tT]-[lL](?:[iI][nN][eE'
|
||||||
|
r']|[eE][tT][tT][eE][rR]))((?:[\r\n\f\040\t]|(?:/\*[^*]*\*+(?:[^/*]['
|
||||||
|
r'^*]*\*+)*/))*)(?=[{,])|((?:(?:\047[^\047\\\r\n\f]*(?:\\(?:[^\r]|\r'
|
||||||
|
r'\n?)[^\047\\\r\n\f]*)*\047)|(?:"[^"\\\r\n\f]*(?:\\(?:[^\r]|\r\n?)['
|
||||||
|
r'^"\\\r\n\f]*)*")))|((?:\\(?:[0-9a-fA-F]{1,6}(?:[\040\n\t\f]|\r\n?)'
|
||||||
|
r'?|[^\n\r\f0-9a-fA-F]))[^\\"\047u>@\r\n\f\040\t/;:{}]*)'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Related
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
- `Javascript Minifier rJSmin <http://opensource.perlig.de/rjsmin/>`_
|
||||||
|
|
||||||
|
|
||||||
|
.. vim: ft=rest tw=72
|
51
docs/_userdoc/website_download.txt
Normal file
51
docs/_userdoc/website_download.txt
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
Download
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
Source Packages
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. begin stable
|
||||||
|
|
||||||
|
Current Stable Version
|
||||||
|
''''''''''''''''''''''
|
||||||
|
|
||||||
|
- `rcssmin-1.0.2.tar.lzma <http://storage.perlig.de/rcssmin/rcssmin-1.0.2.tar.lzma>`_
|
||||||
|
- `rcssmin-1.0.2.tar.bz2 <http://storage.perlig.de/rcssmin/rcssmin-1.0.2.tar.bz2>`_
|
||||||
|
- `rcssmin-1.0.2.tar.gz <http://storage.perlig.de/rcssmin/rcssmin-1.0.2.tar.gz>`_
|
||||||
|
- `rcssmin-1.0.2.zip <http://storage.perlig.de/rcssmin/rcssmin-1.0.2.zip>`_
|
||||||
|
- `rcssmin-1.0.2.digests <http://storage.perlig.de/rcssmin/rcssmin-1.0.2.digests>`_
|
||||||
|
|
||||||
|
.. end stable
|
||||||
|
|
||||||
|
|
||||||
|
Integrity Check
|
||||||
|
---------------
|
||||||
|
|
||||||
|
There are hashes (MD5, SHA1 and SHA256) of the download packages stored
|
||||||
|
in the `digests file
|
||||||
|
<http://storage.perlig.de/rcssmin/rcssmin-1.0.2.digests>`_\.
|
||||||
|
In order to check the integrity of the downloaded file, use a tool like
|
||||||
|
md5sum (or sha1sum, sha256sum accordingly), e.g.:
|
||||||
|
|
||||||
|
.. sourcecode:: console
|
||||||
|
|
||||||
|
$ md5sum -c rcssmin-1.0.2.digests
|
||||||
|
rcssmin-1.0.2.tar.bz2: OK
|
||||||
|
rcssmin-1.0.2.tar.gz: OK
|
||||||
|
rcssmin-1.0.2.tar.lzma: OK
|
||||||
|
rcssmin-1.0.2.zip: OK
|
||||||
|
|
||||||
|
In order to check the integrity of the digest file itself, you can check
|
||||||
|
the PGP signature of that file. The file is signed by André Malo, Key-ID
|
||||||
|
0x8103A37E:
|
||||||
|
|
||||||
|
.. sourcecode:: console
|
||||||
|
|
||||||
|
$ gpg --verify rcssmin-1.0.2.digests
|
||||||
|
gpg: Signature made Sun Feb 10 22:26:25 2013 CET using DSA key ID 8103A37E
|
||||||
|
gpg: Good signature from "Andre Malo <nd@apache.org>"
|
||||||
|
gpg: aka "Andr\xe9\x20Malo <nd@perlig.de>"
|
||||||
|
gpg: aka "Andre Malo <ndparker@gmx.net>"
|
||||||
|
|
||||||
|
|
||||||
|
.. vim: ft=rest tw=72
|
51
docs/_userdoc/website_download.txt.in
Normal file
51
docs/_userdoc/website_download.txt.in
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
Download
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
Source Packages
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. begin stable
|
||||||
|
|
||||||
|
Current Stable Version
|
||||||
|
''''''''''''''''''''''
|
||||||
|
|
||||||
|
- `rcssmin-@@VERSION@@.tar.lzma <http://storage.perlig.de/rcssmin/rcssmin-@@VERSION@@.tar.lzma>`_
|
||||||
|
- `rcssmin-@@VERSION@@.tar.bz2 <http://storage.perlig.de/rcssmin/rcssmin-@@VERSION@@.tar.bz2>`_
|
||||||
|
- `rcssmin-@@VERSION@@.tar.gz <http://storage.perlig.de/rcssmin/rcssmin-@@VERSION@@.tar.gz>`_
|
||||||
|
- `rcssmin-@@VERSION@@.zip <http://storage.perlig.de/rcssmin/rcssmin-@@VERSION@@.zip>`_
|
||||||
|
- `rcssmin-@@VERSION@@.digests <http://storage.perlig.de/rcssmin/rcssmin-@@VERSION@@.digests>`_
|
||||||
|
|
||||||
|
.. end stable
|
||||||
|
|
||||||
|
|
||||||
|
Integrity Check
|
||||||
|
---------------
|
||||||
|
|
||||||
|
There are hashes (MD5, SHA1 and SHA256) of the download packages stored
|
||||||
|
in the `digests file
|
||||||
|
<http://storage.perlig.de/rcssmin/@@PATH@@rcssmin-@@VERSION@@.digests>`_\.
|
||||||
|
In order to check the integrity of the downloaded file, use a tool like
|
||||||
|
md5sum (or sha1sum, sha256sum accordingly), e.g.:
|
||||||
|
|
||||||
|
.. sourcecode:: console
|
||||||
|
|
||||||
|
$ md5sum -c rcssmin-@@VERSION@@.digests
|
||||||
|
rcssmin-@@VERSION@@.tar.bz2: OK
|
||||||
|
rcssmin-@@VERSION@@.tar.gz: OK
|
||||||
|
rcssmin-@@VERSION@@.tar.lzma: OK
|
||||||
|
rcssmin-@@VERSION@@.zip: OK
|
||||||
|
|
||||||
|
In order to check the integrity of the digest file itself, you can check
|
||||||
|
the PGP signature of that file. The file is signed by André Malo, Key-ID
|
||||||
|
0x8103A37E:
|
||||||
|
|
||||||
|
.. sourcecode:: console
|
||||||
|
|
||||||
|
$ gpg --verify rcssmin-@@VERSION@@.digests
|
||||||
|
gpg: Signature made Sun Feb 10 22:26:25 2013 CET using DSA key ID 8103A37E
|
||||||
|
gpg: Good signature from "Andre Malo <nd@apache.org>"
|
||||||
|
gpg: aka "Andr\xe9\x20Malo <nd@perlig.de>"
|
||||||
|
gpg: aka "Andre Malo <ndparker@gmx.net>"
|
||||||
|
|
||||||
|
|
||||||
|
.. vim: ft=rest tw=72
|
22
docs/epydoc.conf
Normal file
22
docs/epydoc.conf
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[epydoc]
|
||||||
|
|
||||||
|
verbosity = 1
|
||||||
|
modules =
|
||||||
|
rcssmin
|
||||||
|
|
||||||
|
output = html
|
||||||
|
url = http://opensource.perlig.de/rcssmin/
|
||||||
|
link = <a href="http://opensource.perlig.de/rcssmin/" target="_top">Visit rcssmin Online</a>
|
||||||
|
top = rcssmin
|
||||||
|
frames = no
|
||||||
|
sourcecode = yes
|
||||||
|
|
||||||
|
#graphs are ugly
|
||||||
|
#graph = classtree
|
||||||
|
|
||||||
|
target = docs/apidoc/
|
||||||
|
docformat = plaintext
|
||||||
|
|
||||||
|
private = no
|
||||||
|
parse = yes
|
||||||
|
introspect = yes
|
57
gen_chartable.py
Executable file
57
gen_chartable.py
Executable file
@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import re as _re
|
||||||
|
|
||||||
|
TPL = r"""
|
||||||
|
static const unsigned short rcssmin_charmask[128] = {
|
||||||
|
@@mask@@
|
||||||
|
};
|
||||||
|
""".strip() + "\n"
|
||||||
|
|
||||||
|
def _make_charmask():
|
||||||
|
dull = r'[^\\"\047u>@\r\n\f\040\t/;:{}]'
|
||||||
|
hexchar = r'[0-9a-fA-F]'
|
||||||
|
escaped = r'[^\n\r\f0-9a-fA-F]'
|
||||||
|
space = r'[\040\t\r\n\f]'
|
||||||
|
string_dull = r'[^"\047\\\r\n\f]'
|
||||||
|
nmchar = r'[^\000-\054\056\057\072-\100\133-\136\140\173-\177]'
|
||||||
|
uri_dull = r'[^\000-\040"\047()\\\177]'
|
||||||
|
pre_char = r'[{}(=:>+[,!]'
|
||||||
|
post_char = r'[:{});=>+\],!]'
|
||||||
|
|
||||||
|
charmask = []
|
||||||
|
for x in range(8):
|
||||||
|
maskline = []
|
||||||
|
for y in range(16):
|
||||||
|
c, mask = chr(x*16 + y), 0
|
||||||
|
if _re.match(dull, c):
|
||||||
|
mask |= 1
|
||||||
|
if _re.match(hexchar, c):
|
||||||
|
mask |= 2
|
||||||
|
if _re.match(escaped, c):
|
||||||
|
mask |= 4
|
||||||
|
if _re.match(space, c):
|
||||||
|
mask |= 8
|
||||||
|
if _re.match(string_dull, c):
|
||||||
|
mask |= 16
|
||||||
|
if _re.match(nmchar, c):
|
||||||
|
mask |= 32
|
||||||
|
if _re.match(uri_dull, c):
|
||||||
|
mask |= 64
|
||||||
|
if _re.match(pre_char, c):
|
||||||
|
mask |= 128
|
||||||
|
if _re.match(post_char, c):
|
||||||
|
mask |= 256
|
||||||
|
|
||||||
|
if mask < 10:
|
||||||
|
mask = ' ' + str(mask)
|
||||||
|
elif mask < 100:
|
||||||
|
mask = ' ' + str(mask)
|
||||||
|
maskline.append(str(mask))
|
||||||
|
if y == 7:
|
||||||
|
charmask.append(', '.join(maskline))
|
||||||
|
maskline = []
|
||||||
|
charmask.append(', '.join(maskline))
|
||||||
|
return TPL.replace('@@mask@@', ',\n '.join(charmask))
|
||||||
|
|
||||||
|
print _make_charmask()
|
705
make.py
Executable file
705
make.py
Executable file
@ -0,0 +1,705 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2006 - 2014
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
===============
|
||||||
|
Build targets
|
||||||
|
===============
|
||||||
|
|
||||||
|
Build targets.
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__author__ = getattr(__author__, 'decode', lambda x: __author__)('latin-1')
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
|
||||||
|
import errno as _errno
|
||||||
|
import os as _os
|
||||||
|
import re as _re
|
||||||
|
import sys as _sys
|
||||||
|
|
||||||
|
from _setup import dist
|
||||||
|
from _setup import shell
|
||||||
|
from _setup import make
|
||||||
|
from _setup import term
|
||||||
|
from _setup.make import targets, default_targets
|
||||||
|
|
||||||
|
|
||||||
|
class Target(make.Target):
|
||||||
|
def init(self):
|
||||||
|
self.dirs = {
|
||||||
|
'lib': '.',
|
||||||
|
'docs': 'docs',
|
||||||
|
'apidoc': 'docs/apidoc',
|
||||||
|
'userdoc': 'docs/userdoc',
|
||||||
|
'userdoc_source': 'docs/_userdoc',
|
||||||
|
'userdoc_build': 'docs/_userdoc/_build',
|
||||||
|
'website': 'dist/website',
|
||||||
|
'_website': '_website', # source dir
|
||||||
|
'tests': 'tests',
|
||||||
|
'dist': 'dist',
|
||||||
|
'build': 'build',
|
||||||
|
'bench': 'bench',
|
||||||
|
'ebuild': '_pkg/ebuilds',
|
||||||
|
}
|
||||||
|
libpath = shell.native(self.dirs['lib'])
|
||||||
|
if libpath != _sys.path[0]:
|
||||||
|
while libpath in _sys.path:
|
||||||
|
_sys.path.remove(libpath)
|
||||||
|
_sys.path.insert(0, libpath)
|
||||||
|
|
||||||
|
self.ebuild_files = {
|
||||||
|
'rcssmin.ebuild.in':
|
||||||
|
'rcssmin-%(VERSION)s.ebuild',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Manifest = targets.Manifest
|
||||||
|
|
||||||
|
class Distribution(targets.Distribution):
|
||||||
|
def init(self):
|
||||||
|
self._dist = 'dist'
|
||||||
|
self._ebuilds = '_pkg/ebuilds'
|
||||||
|
self._changes = 'docs/CHANGES'
|
||||||
|
|
||||||
|
|
||||||
|
class Check(Target):
|
||||||
|
""" Check the python code """
|
||||||
|
NAME = "check"
|
||||||
|
DEPS = ["compile-quiet"]
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
from _setup.dev import analysis
|
||||||
|
term.green('Linting rcssmin sources...')
|
||||||
|
res = analysis.pylint('_pkg/pylint.conf', 'rcssmin')
|
||||||
|
if res == 2:
|
||||||
|
make.warn('pylint not found', self.NAME)
|
||||||
|
|
||||||
|
|
||||||
|
class Test(Target):
|
||||||
|
""" Run the system tests """
|
||||||
|
NAME = "test"
|
||||||
|
DEPS = ["compile-quiet"]
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
run_output_tests(self.dirs['tests'])
|
||||||
|
|
||||||
|
|
||||||
|
def run_output_tests(basedir):
|
||||||
|
""" Run output based tests """
|
||||||
|
import rcssmin as _rcssmin
|
||||||
|
py_cssmin = _rcssmin._make_cssmin(python_only=True)
|
||||||
|
c_cssmin = _rcssmin._make_cssmin(python_only=False)
|
||||||
|
|
||||||
|
def run_test(example, output_file):
|
||||||
|
""" Run it """
|
||||||
|
try:
|
||||||
|
fp = open(example, 'r')
|
||||||
|
except IOError:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
input = fp.read()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
def load_output(filename):
|
||||||
|
try:
|
||||||
|
fp = open(filename, 'r')
|
||||||
|
except IOError:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
output = fp.read()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
output = output.strip()
|
||||||
|
if _re.search(r'(?<!\\)(?:\\\\)*\\[0-9a-zA-Z]{1,6}$', output):
|
||||||
|
output += ' '
|
||||||
|
return output
|
||||||
|
|
||||||
|
output = load_output(output_file)
|
||||||
|
output_b = load_output(output_file + '.b')
|
||||||
|
|
||||||
|
def do_test(cssmin, output, **options):
|
||||||
|
try:
|
||||||
|
genout = cssmin(input, **options)
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
return "%(RED)s exc%(NORMAL)s "
|
||||||
|
else:
|
||||||
|
if output is None:
|
||||||
|
return "%(RED)smiss%(NORMAL)s "
|
||||||
|
elif genout == output or genout == output.rstrip():
|
||||||
|
return "%(GREEN)sOK%(NORMAL)s "
|
||||||
|
else:
|
||||||
|
return "%(RED)sfail%(NORMAL)s "
|
||||||
|
|
||||||
|
out = do_test(py_cssmin, output)
|
||||||
|
c_out = do_test(c_cssmin, output)
|
||||||
|
out_b = do_test(py_cssmin, output_b, keep_bang_comments=True)
|
||||||
|
c_out_b = do_test(c_cssmin, output_b, keep_bang_comments=True)
|
||||||
|
|
||||||
|
term.write(
|
||||||
|
"%(out)s %(out_b)s | %(c_out)s %(c_out_b)s - %%(example)s\n"
|
||||||
|
% locals(),
|
||||||
|
example=_os.path.basename(example),
|
||||||
|
)
|
||||||
|
|
||||||
|
# end
|
||||||
|
# begin main test code
|
||||||
|
|
||||||
|
basedir = shell.native(basedir)
|
||||||
|
strip = len(basedir) - len(_os.path.basename(basedir))
|
||||||
|
for dirname, dirs, files in shell.walk(basedir):
|
||||||
|
dirs[:] = [item for item in dirs if item not in ('.svn', 'out')]
|
||||||
|
dirs.sort()
|
||||||
|
files = [item for item in files if item.endswith('.css')]
|
||||||
|
if not files:
|
||||||
|
continue
|
||||||
|
if not _os.path.isdir(_os.path.join(basedir, dirname, 'out')):
|
||||||
|
continue
|
||||||
|
term.yellow("---> %s" % (dirname[strip:],))
|
||||||
|
files.sort()
|
||||||
|
for filename in files:
|
||||||
|
run_test(
|
||||||
|
_os.path.join(dirname, filename),
|
||||||
|
_os.path.join(dirname, 'out', filename[:-4] + '.out'),
|
||||||
|
)
|
||||||
|
term.yellow("<--- %s" % (dirname[strip:],))
|
||||||
|
|
||||||
|
|
||||||
|
class Benchmark(Target):
|
||||||
|
""" Benchmark """
|
||||||
|
NAME = "bench"
|
||||||
|
DEPS = ["compile-quiet"]
|
||||||
|
python = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
files = list(shell.files(self.dirs['bench'], '*.css'))
|
||||||
|
if self.python is None:
|
||||||
|
python = _sys.executable
|
||||||
|
else:
|
||||||
|
python = shell.frompath(self.python)
|
||||||
|
return not shell.spawn(*[
|
||||||
|
python,
|
||||||
|
shell.native('bench.py'),
|
||||||
|
'-c10'
|
||||||
|
] + files)
|
||||||
|
|
||||||
|
def clean(self, scm, dist):
|
||||||
|
term.green("Removing bytecode files...")
|
||||||
|
for filename in shell.dirs('.', '__pycache__'):
|
||||||
|
shell.rm_rf(filename)
|
||||||
|
for filename in shell.files('.', '*.py[co]'):
|
||||||
|
shell.rm(filename)
|
||||||
|
for filename in shell.files('.', '*$py.class'):
|
||||||
|
shell.rm(filename)
|
||||||
|
|
||||||
|
|
||||||
|
class Benchmark2(Benchmark):
|
||||||
|
""" Benchmark """
|
||||||
|
NAME = "bench2"
|
||||||
|
python = "python2"
|
||||||
|
|
||||||
|
def clean(self, scm, dist):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Benchmark3(Benchmark):
|
||||||
|
""" Benchmark """
|
||||||
|
NAME = "bench3"
|
||||||
|
python = "python3"
|
||||||
|
|
||||||
|
def clean(self, scm, dist):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Compile(Target):
|
||||||
|
""" Compile the python code """
|
||||||
|
NAME = "compile"
|
||||||
|
#DEPS = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
import setup
|
||||||
|
|
||||||
|
_old_argv = _sys.argv
|
||||||
|
try:
|
||||||
|
_sys.argv = ['setup.py', '-q', 'build']
|
||||||
|
if not self.HIDDEN:
|
||||||
|
_sys.argv.remove('-q')
|
||||||
|
setup.setup()
|
||||||
|
if 'java' not in _sys.platform.lower():
|
||||||
|
_sys.argv = [
|
||||||
|
'setup.py', '-q', 'install_lib', '--install-dir',
|
||||||
|
shell.native(self.dirs['lib']),
|
||||||
|
'--optimize', '2',
|
||||||
|
]
|
||||||
|
if not self.HIDDEN:
|
||||||
|
_sys.argv.remove('-q')
|
||||||
|
setup.setup()
|
||||||
|
finally:
|
||||||
|
_sys.argv = _old_argv
|
||||||
|
|
||||||
|
self.compile('rcssmin.py')
|
||||||
|
term.write("%(ERASE)s")
|
||||||
|
|
||||||
|
term.green("All files successfully compiled.")
|
||||||
|
|
||||||
|
def compile(self, name):
|
||||||
|
path = shell.native(name)
|
||||||
|
term.write("%(ERASE)s%(BOLD)s>>> Compiling %(name)s...%(NORMAL)s",
|
||||||
|
name=name)
|
||||||
|
from distutils import util
|
||||||
|
try:
|
||||||
|
from distutils import log
|
||||||
|
except ImportError:
|
||||||
|
util.byte_compile([path], verbose=0, force=True)
|
||||||
|
else:
|
||||||
|
log.set_verbosity(0)
|
||||||
|
util.byte_compile([path], force=True)
|
||||||
|
|
||||||
|
def clean(self, scm, dist):
|
||||||
|
term.green("Removing python byte code...")
|
||||||
|
for name in shell.dirs('.', '__pycache__'):
|
||||||
|
shell.rm_rf(name)
|
||||||
|
for name in shell.files('.', '*.py[co]'):
|
||||||
|
shell.rm(name)
|
||||||
|
for name in shell.files('.', '*$py.class'):
|
||||||
|
shell.rm(name)
|
||||||
|
|
||||||
|
term.green("Removing c extensions...")
|
||||||
|
for name in shell.files('.', '*.so'):
|
||||||
|
shell.rm(name)
|
||||||
|
for name in shell.files('.', '*.pyd'):
|
||||||
|
shell.rm(name)
|
||||||
|
|
||||||
|
shell.rm_rf(self.dirs['build'])
|
||||||
|
|
||||||
|
|
||||||
|
class CompileQuiet(Compile):
|
||||||
|
NAME = "compile-quiet"
|
||||||
|
HIDDEN = True
|
||||||
|
|
||||||
|
def clean(self, scm, dist):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Doc(Target):
|
||||||
|
""" Build the docs (api + user) """
|
||||||
|
NAME = "doc"
|
||||||
|
DEPS = ['apidoc', 'userdoc']
|
||||||
|
|
||||||
|
|
||||||
|
class ApiDoc(Target):
|
||||||
|
""" Build the API docs """
|
||||||
|
NAME = "apidoc"
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
from _setup.dev import apidoc
|
||||||
|
apidoc.epydoc(
|
||||||
|
prepend=[
|
||||||
|
shell.native(self.dirs['lib']),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean(self, scm, dist):
|
||||||
|
if scm:
|
||||||
|
term.green("Removing apidocs...")
|
||||||
|
shell.rm_rf(self.dirs['apidoc'])
|
||||||
|
|
||||||
|
|
||||||
|
class UserDoc(Target):
|
||||||
|
""" Build the user docs """
|
||||||
|
NAME = "userdoc"
|
||||||
|
#DEPS = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
from _setup.dev import userdoc
|
||||||
|
userdoc.sphinx(
|
||||||
|
build=shell.native(self.dirs['userdoc_build']),
|
||||||
|
source=shell.native(self.dirs['userdoc_source']),
|
||||||
|
target=shell.native(self.dirs['userdoc']),
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean(self, scm, dist):
|
||||||
|
if scm:
|
||||||
|
term.green("Removing userdocs...")
|
||||||
|
shell.rm_rf(self.dirs['userdoc'])
|
||||||
|
shell.rm_rf(self.dirs['userdoc_build'])
|
||||||
|
|
||||||
|
|
||||||
|
class Website(Target):
|
||||||
|
""" Build the website """
|
||||||
|
NAME = "website"
|
||||||
|
DEPS = ["apidoc"]
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
from _setup.util import SafeConfigParser as parser
|
||||||
|
parser = parser()
|
||||||
|
parser.read('package.cfg')
|
||||||
|
strversion = parser.get('package', 'version.number')
|
||||||
|
shortversion = tuple(map(int, strversion.split('.')[:2]))
|
||||||
|
|
||||||
|
shell.rm_rf(self.dirs['_website'])
|
||||||
|
shell.cp_r(
|
||||||
|
self.dirs['userdoc_source'],
|
||||||
|
_os.path.join(self.dirs['_website'], 'src')
|
||||||
|
)
|
||||||
|
shell.rm_rf(_os.path.join(self.dirs['_website'], 'build'))
|
||||||
|
shell.rm_rf(self.dirs['website'])
|
||||||
|
_os.makedirs(self.dirs['website'])
|
||||||
|
filename = _os.path.join(
|
||||||
|
self.dirs['_website'], 'src', 'website_download.txt'
|
||||||
|
)
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
download = fp.read()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
filename = _os.path.join(self.dirs['_website'], 'src', 'index.txt')
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
indexlines = fp.readlines()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
fp = open(filename, 'w')
|
||||||
|
try:
|
||||||
|
for line in indexlines:
|
||||||
|
if line.startswith('.. placeholder: Download'):
|
||||||
|
line = download
|
||||||
|
fp.write(line)
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
shell.cp_r(
|
||||||
|
self.dirs['apidoc'],
|
||||||
|
_os.path.join(self.dirs['website'], 'doc-%d.%d' % shortversion)
|
||||||
|
)
|
||||||
|
shell.cp_r(
|
||||||
|
self.dirs['apidoc'],
|
||||||
|
_os.path.join(
|
||||||
|
self.dirs['_website'], 'src', 'doc-%d.%d' % shortversion
|
||||||
|
)
|
||||||
|
)
|
||||||
|
fp = open(_os.path.join(
|
||||||
|
self.dirs['_website'], 'src', 'conf.py'
|
||||||
|
), 'a')
|
||||||
|
try:
|
||||||
|
fp.write("\nepydoc = dict(rcssmin=%r)\n" % (
|
||||||
|
_os.path.join(
|
||||||
|
shell.native(self.dirs['_website']),
|
||||||
|
"src",
|
||||||
|
"doc-%d.%d" % shortversion,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
fp.write("\nexclude_trees.append(%r)\n" %
|
||||||
|
"doc-%d.%d" % shortversion
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
from _setup.dev import userdoc
|
||||||
|
userdoc.sphinx(
|
||||||
|
build=shell.native(_os.path.join(self.dirs['_website'], 'build')),
|
||||||
|
source=shell.native(_os.path.join(self.dirs['_website'], 'src')),
|
||||||
|
target=shell.native(self.dirs['website']),
|
||||||
|
)
|
||||||
|
shell.rm(_os.path.join(self.dirs['website'], '.buildinfo'))
|
||||||
|
|
||||||
|
def clean(self, scm, dist):
|
||||||
|
if scm:
|
||||||
|
term.green("Removing website...")
|
||||||
|
shell.rm_rf(self.dirs['website'])
|
||||||
|
shell.rm_rf(self.dirs['_website'])
|
||||||
|
|
||||||
|
|
||||||
|
class PreCheck(Target):
|
||||||
|
""" Run clean, doc, check """
|
||||||
|
NAME = "precheck"
|
||||||
|
DEPS = ["clean", "doc", "check", "test"]
|
||||||
|
|
||||||
|
|
||||||
|
class SVNRelease(Target):
|
||||||
|
""" Release current version """
|
||||||
|
#NAME = "release"
|
||||||
|
DEPS = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self._check_committed()
|
||||||
|
self._update_versions()
|
||||||
|
self._tag_release()
|
||||||
|
self.runner('dist', seen={})
|
||||||
|
|
||||||
|
def _tag_release(self):
|
||||||
|
""" Tag release """
|
||||||
|
from _setup.util import SafeConfigParser as parser
|
||||||
|
parser = parser()
|
||||||
|
parser.read('package.cfg')
|
||||||
|
strversion = parser.get('package', 'version.number')
|
||||||
|
version = strversion
|
||||||
|
trunk_url = self._repo_url()
|
||||||
|
if not trunk_url.endswith('/trunk'):
|
||||||
|
rex = _re.compile(r'/branches/\d+(?:\.\d+)*\.[xX]$').search
|
||||||
|
match = rex(trunk_url)
|
||||||
|
if not match:
|
||||||
|
make.fail("Not in trunk or release branch!")
|
||||||
|
found = match.start(0)
|
||||||
|
else:
|
||||||
|
found = -len('/trunk')
|
||||||
|
release_url = trunk_url[:found] + '/releases/' + version
|
||||||
|
|
||||||
|
svn = shell.frompath('svn')
|
||||||
|
shell.spawn(
|
||||||
|
svn, 'copy', '-m', 'Release version ' + version, '--',
|
||||||
|
trunk_url, release_url,
|
||||||
|
echo=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _update_versions(self):
|
||||||
|
""" Update versions """
|
||||||
|
self.runner('version', seen={})
|
||||||
|
svn = shell.frompath('svn')
|
||||||
|
shell.spawn(svn, 'commit', '-m', 'Pre-release: version update',
|
||||||
|
echo=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def _repo_url(self):
|
||||||
|
""" Determine URL """
|
||||||
|
from xml.dom import minidom
|
||||||
|
svn = shell.frompath('svn')
|
||||||
|
info = minidom.parseString(
|
||||||
|
shell.spawn(svn, 'info', '--xml', stdout=True)
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
url = info.getElementsByTagName('url')[0]
|
||||||
|
text = []
|
||||||
|
for node in url.childNodes:
|
||||||
|
if node.nodeType == node.TEXT_NODE:
|
||||||
|
text.append(node.data)
|
||||||
|
finally:
|
||||||
|
info.unlink()
|
||||||
|
return ''.join(text).encode('utf-8')
|
||||||
|
|
||||||
|
def _check_committed(self):
|
||||||
|
""" Check if everything is committed """
|
||||||
|
if not self._repo_url().endswith('/trunk'):
|
||||||
|
rex = _re.compile(r'/branches/\d+(?:\.\d+)*\.[xX]$').search
|
||||||
|
match = rex(self._repo_url())
|
||||||
|
if not match:
|
||||||
|
make.fail("Not in trunk or release branch!")
|
||||||
|
svn = shell.frompath('svn')
|
||||||
|
lines = shell.spawn(svn, 'stat', '--ignore-externals',
|
||||||
|
stdout=True, env=dict(_os.environ, LC_ALL='C'),
|
||||||
|
).splitlines()
|
||||||
|
for line in lines:
|
||||||
|
if line.startswith('X'):
|
||||||
|
continue
|
||||||
|
make.fail("Uncommitted changes!")
|
||||||
|
|
||||||
|
|
||||||
|
class GitRelease(Target):
|
||||||
|
""" Release current version """
|
||||||
|
#NAME = "release"
|
||||||
|
DEPS = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self._check_committed()
|
||||||
|
self._update_versions()
|
||||||
|
self._tag_release()
|
||||||
|
self.runner('dist', seen={})
|
||||||
|
|
||||||
|
def _tag_release(self):
|
||||||
|
""" Tag release """
|
||||||
|
from _setup.util import SafeConfigParser as parser
|
||||||
|
parser = parser()
|
||||||
|
parser.read('package.cfg')
|
||||||
|
strversion = parser.get('package', 'version.number')
|
||||||
|
version = strversion
|
||||||
|
git = shell.frompath('git')
|
||||||
|
shell.spawn(
|
||||||
|
git, 'tag', '-a', '-m', 'Release version ' + version, '--',
|
||||||
|
version,
|
||||||
|
echo=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _update_versions(self):
|
||||||
|
""" Update versions """
|
||||||
|
self.runner('version', seen={})
|
||||||
|
git = shell.frompath('git')
|
||||||
|
shell.spawn(git, 'commit', '-a', '-m', 'Pre-release: version update',
|
||||||
|
echo=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def _check_committed(self):
|
||||||
|
""" Check if everything is committed """
|
||||||
|
git = shell.frompath('git')
|
||||||
|
lines = shell.spawn(git, 'branch', '--color=never',
|
||||||
|
stdout=True, env=dict(_os.environ, LC_ALL='C')
|
||||||
|
).splitlines()
|
||||||
|
for line in lines:
|
||||||
|
if line.startswith('*'):
|
||||||
|
branch = line.split(None, 1)[1]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
make.fail("Could not determine current branch.")
|
||||||
|
if branch != 'master':
|
||||||
|
rex = _re.compile(r'^\d+(?:\.\d+)*\.[xX]$').match
|
||||||
|
match = rex(branch)
|
||||||
|
if not match:
|
||||||
|
make.fail("Not in master or release branch.")
|
||||||
|
|
||||||
|
lines = shell.spawn(git, 'status', '--porcelain',
|
||||||
|
stdout=True, env=dict(_os.environ, LC_ALL='C'),
|
||||||
|
)
|
||||||
|
if lines:
|
||||||
|
make.fail("Uncommitted changes!")
|
||||||
|
|
||||||
|
|
||||||
|
class Release(GitRelease):
|
||||||
|
NAME = "release"
|
||||||
|
#DEPS = None
|
||||||
|
|
||||||
|
|
||||||
|
class Version(Target):
|
||||||
|
""" Insert the program version into all relevant files """
|
||||||
|
NAME = "version"
|
||||||
|
#DEPS = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
from _setup.util import SafeConfigParser as parser
|
||||||
|
parser = parser()
|
||||||
|
parser.read('package.cfg')
|
||||||
|
strversion = parser.get('package', 'version.number')
|
||||||
|
|
||||||
|
self._version_init(strversion)
|
||||||
|
self._version_userdoc(strversion)
|
||||||
|
self._version_download(strversion)
|
||||||
|
self._version_changes(strversion)
|
||||||
|
|
||||||
|
parm = {'VERSION': strversion}
|
||||||
|
for src, dest in self.ebuild_files.items():
|
||||||
|
src = "%s/%s" % (self.dirs['ebuild'], src)
|
||||||
|
dest = "%s/%s" % (self.dirs['ebuild'], dest % parm)
|
||||||
|
term.green("Creating %(name)s...", name=dest)
|
||||||
|
shell.cp(src, dest)
|
||||||
|
|
||||||
|
def _version_init(self, strversion):
|
||||||
|
""" Modify version in __init__ """
|
||||||
|
filename = _os.path.join(self.dirs['lib'], 'rcssmin.py')
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
initlines = fp.readlines()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
fp = open(filename, 'w')
|
||||||
|
replaced = False
|
||||||
|
try:
|
||||||
|
for line in initlines:
|
||||||
|
if line.startswith('__version__'):
|
||||||
|
line = '__version__ = %r\n' % (strversion,)
|
||||||
|
replaced = True
|
||||||
|
fp.write(line)
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
assert replaced, "__version__ not found in rcssmin.py"
|
||||||
|
|
||||||
|
def _version_changes(self, strversion):
|
||||||
|
""" Modify version in changes """
|
||||||
|
filename = _os.path.join(shell.native(self.dirs['docs']), 'CHANGES')
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
initlines = fp.readlines()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
fp = open(filename, 'w')
|
||||||
|
try:
|
||||||
|
for line in initlines:
|
||||||
|
if line.rstrip() == "Changes with version":
|
||||||
|
line = "%s %s\n" % (line.rstrip(), strversion)
|
||||||
|
fp.write(line)
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
def _version_userdoc(self, strversion):
|
||||||
|
""" Modify version in userdoc """
|
||||||
|
filename = _os.path.join(self.dirs['userdoc_source'], 'conf.py')
|
||||||
|
shortversion = '.'.join(strversion.split('.')[:2])
|
||||||
|
longversion = strversion
|
||||||
|
fp = open(filename)
|
||||||
|
try:
|
||||||
|
initlines = fp.readlines()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
replaced = 0
|
||||||
|
fp = open(filename, 'w')
|
||||||
|
try:
|
||||||
|
for line in initlines:
|
||||||
|
if line.startswith('version'):
|
||||||
|
line = 'version = %r\n' % shortversion
|
||||||
|
replaced |= 1
|
||||||
|
elif line.startswith('release'):
|
||||||
|
line = 'release = %r\n' % longversion
|
||||||
|
replaced |= 2
|
||||||
|
fp.write(line)
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
assert replaced & 3 != 0, "version/release not found in conf.py"
|
||||||
|
|
||||||
|
def _version_download(self, strversion):
|
||||||
|
""" Modify version in website download docs """
|
||||||
|
filename = _os.path.join(
|
||||||
|
self.dirs['userdoc_source'], 'website_download.txt'
|
||||||
|
)
|
||||||
|
VERSION, PATH = strversion, ''
|
||||||
|
fp = open(filename + '.in')
|
||||||
|
try:
|
||||||
|
dllines = fp.readlines()
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
instable = []
|
||||||
|
fp = open(filename, 'w')
|
||||||
|
try:
|
||||||
|
for line in dllines:
|
||||||
|
if instable:
|
||||||
|
instable.append(line)
|
||||||
|
if line.startswith('.. end stable'):
|
||||||
|
res = (''.join(instable)
|
||||||
|
.replace('@@VERSION@@', strversion)
|
||||||
|
.replace('@@PATH@@', '')
|
||||||
|
)
|
||||||
|
fp.write(res)
|
||||||
|
instable = []
|
||||||
|
elif line.startswith('.. begin stable'):
|
||||||
|
instable.append(line)
|
||||||
|
else:
|
||||||
|
fp.write(line
|
||||||
|
.replace('@@VERSION@@', VERSION)
|
||||||
|
.replace('@@PATH@@', PATH)
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
def clean(self, scm, dist):
|
||||||
|
""" Clean versioned files """
|
||||||
|
if scm:
|
||||||
|
term.green("Removing generated ebuild files")
|
||||||
|
for name in shell.files(self.dirs['ebuild'], '*.ebuild'):
|
||||||
|
shell.rm(name)
|
||||||
|
|
||||||
|
|
||||||
|
make.main(name=__name__)
|
77
package.cfg
Normal file
77
package.cfg
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2009 - 2014
|
||||||
|
# André Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = rcssmin
|
||||||
|
|
||||||
|
python.min = 2.3
|
||||||
|
python.max = 3.3
|
||||||
|
pypy.min = 1.9
|
||||||
|
pypy.max = 2.0
|
||||||
|
jython.min = 2.5
|
||||||
|
jython.max = 2.5
|
||||||
|
|
||||||
|
version.number = 1.0.2
|
||||||
|
|
||||||
|
author.name = André Malo
|
||||||
|
author.email = nd@perlig.de
|
||||||
|
#maintainer.name =
|
||||||
|
#maintainer.email =
|
||||||
|
url.homepage = http://opensource.perlig.de/rcssmin/
|
||||||
|
url.download = http://storage.perlig.de/rcssmin/
|
||||||
|
|
||||||
|
|
||||||
|
[docs]
|
||||||
|
meta.classifiers = docs/CLASSIFIERS
|
||||||
|
meta.description = docs/DESCRIPTION
|
||||||
|
meta.summary = docs/SUMMARY
|
||||||
|
meta.provides = docs/PROVIDES
|
||||||
|
meta.license = LICENSE
|
||||||
|
meta.keywords =
|
||||||
|
CSS
|
||||||
|
Minimization
|
||||||
|
|
||||||
|
apidoc.dir = docs/apidoc
|
||||||
|
apidoc.strip = 1
|
||||||
|
#apidoc.ignore =
|
||||||
|
|
||||||
|
#userdoc.dir = docs/userdoc
|
||||||
|
#userdoc.strip = 1
|
||||||
|
#userdoc.ignore =
|
||||||
|
# .buildinfo
|
||||||
|
|
||||||
|
#examples.dir = docs/examples
|
||||||
|
#examples.strip = 1
|
||||||
|
#examples.ignore =
|
||||||
|
|
||||||
|
#man =
|
||||||
|
|
||||||
|
extra =
|
||||||
|
README.rst
|
||||||
|
docs/CHANGES
|
||||||
|
|
||||||
|
|
||||||
|
[manifest]
|
||||||
|
#packages.lib = .
|
||||||
|
#packages.collect =
|
||||||
|
modules = rcssmin
|
||||||
|
|
||||||
|
#scripts =
|
||||||
|
|
||||||
|
dist =
|
||||||
|
bench.py
|
||||||
|
bench
|
360
rcssmin.py
Executable file
360
rcssmin.py
Executable file
@ -0,0 +1,360 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2011 - 2013
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
r"""
|
||||||
|
==============
|
||||||
|
CSS Minifier
|
||||||
|
==============
|
||||||
|
|
||||||
|
CSS Minifier.
|
||||||
|
|
||||||
|
The minifier is based on the semantics of the `YUI compressor`_\, which itself
|
||||||
|
is based on `the rule list by Isaac Schlueter`_\.
|
||||||
|
|
||||||
|
This module is a re-implementation aiming for speed instead of maximum
|
||||||
|
compression, so it can be used at runtime (rather than during a preprocessing
|
||||||
|
step). RCSSmin does syntactical compression only (removing spaces, comments
|
||||||
|
and possibly semicolons). It does not provide semantic compression (like
|
||||||
|
removing empty blocks, collapsing redundant properties etc). It does, however,
|
||||||
|
support various CSS hacks (by keeping them working as intended).
|
||||||
|
|
||||||
|
Here's a feature list:
|
||||||
|
|
||||||
|
- Strings are kept, except that escaped newlines are stripped
|
||||||
|
- Space/Comments before the very end or before various characters are
|
||||||
|
stripped: ``:{});=>+],!`` (The colon (``:``) is a special case, a single
|
||||||
|
space is kept if it's outside a ruleset.)
|
||||||
|
- Space/Comments at the very beginning or after various characters are
|
||||||
|
stripped: ``{}(=:>+[,!``
|
||||||
|
- Optional space after unicode escapes is kept, resp. replaced by a simple
|
||||||
|
space
|
||||||
|
- whitespaces inside ``url()`` definitions are stripped
|
||||||
|
- Comments starting with an exclamation mark (``!``) can be kept optionally.
|
||||||
|
- All other comments and/or whitespace characters are replaced by a single
|
||||||
|
space.
|
||||||
|
- Multiple consecutive semicolons are reduced to one
|
||||||
|
- The last semicolon within a ruleset is stripped
|
||||||
|
- CSS Hacks supported:
|
||||||
|
|
||||||
|
- IE7 hack (``>/**/``)
|
||||||
|
- Mac-IE5 hack (``/*\*/.../**/``)
|
||||||
|
- The boxmodelhack is supported naturally because it relies on valid CSS2
|
||||||
|
strings
|
||||||
|
- Between ``:first-line`` and the following comma or curly brace a space is
|
||||||
|
inserted. (apparently it's needed for IE6)
|
||||||
|
- Same for ``:first-letter``
|
||||||
|
|
||||||
|
rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to
|
||||||
|
factor 50 or so (depending on the input).
|
||||||
|
|
||||||
|
Both python 2 (>= 2.4) and python 3 are supported.
|
||||||
|
|
||||||
|
.. _YUI compressor: https://github.com/yui/yuicompressor/
|
||||||
|
|
||||||
|
.. _the rule list by Isaac Schlueter: https://github.com/isaacs/cssmin/tree/
|
||||||
|
"""
|
||||||
|
__author__ = "Andr\xe9 Malo"
|
||||||
|
__author__ = getattr(__author__, 'decode', lambda x: __author__)('latin-1')
|
||||||
|
__docformat__ = "restructuredtext en"
|
||||||
|
__license__ = "Apache License, Version 2.0"
|
||||||
|
__version__ = '1.0.2'
|
||||||
|
__all__ = ['cssmin']
|
||||||
|
|
||||||
|
import re as _re
|
||||||
|
|
||||||
|
|
||||||
|
def _make_cssmin(python_only=False):
|
||||||
|
"""
|
||||||
|
Generate CSS minifier.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`python_only` : ``bool``
|
||||||
|
Use only the python variant. If true, the c extension is not even
|
||||||
|
tried to be loaded.
|
||||||
|
|
||||||
|
:Return: Minifier
|
||||||
|
:Rtype: ``callable``
|
||||||
|
"""
|
||||||
|
# pylint: disable = W0612
|
||||||
|
# ("unused" variables)
|
||||||
|
|
||||||
|
# pylint: disable = R0911, R0912, R0914, R0915
|
||||||
|
# (too many anything)
|
||||||
|
|
||||||
|
if not python_only:
|
||||||
|
try:
|
||||||
|
import _rcssmin
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return _rcssmin.cssmin
|
||||||
|
|
||||||
|
nl = r'(?:[\n\f]|\r\n?)' # pylint: disable = C0103
|
||||||
|
spacechar = r'[\r\n\f\040\t]'
|
||||||
|
|
||||||
|
unicoded = r'[0-9a-fA-F]{1,6}(?:[\040\n\t\f]|\r\n?)?'
|
||||||
|
escaped = r'[^\n\r\f0-9a-fA-F]'
|
||||||
|
escape = r'(?:\\(?:%(unicoded)s|%(escaped)s))' % locals()
|
||||||
|
|
||||||
|
nmchar = r'[^\000-\054\056\057\072-\100\133-\136\140\173-\177]'
|
||||||
|
#nmstart = r'[^\000-\100\133-\136\140\173-\177]'
|
||||||
|
#ident = (r'(?:'
|
||||||
|
# r'-?(?:%(nmstart)s|%(escape)s)%(nmchar)s*(?:%(escape)s%(nmchar)s*)*'
|
||||||
|
#r')') % locals()
|
||||||
|
|
||||||
|
comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)'
|
||||||
|
|
||||||
|
# only for specific purposes. The bang is grouped:
|
||||||
|
_bang_comment = r'(?:/\*(!?)[^*]*\*+(?:[^/*][^*]*\*+)*/)'
|
||||||
|
|
||||||
|
string1 = \
|
||||||
|
r'(?:\047[^\047\\\r\n\f]*(?:\\[^\r\n\f][^\047\\\r\n\f]*)*\047)'
|
||||||
|
string2 = r'(?:"[^"\\\r\n\f]*(?:\\[^\r\n\f][^"\\\r\n\f]*)*")'
|
||||||
|
strings = r'(?:%s|%s)' % (string1, string2)
|
||||||
|
|
||||||
|
nl_string1 = \
|
||||||
|
r'(?:\047[^\047\\\r\n\f]*(?:\\(?:[^\r]|\r\n?)[^\047\\\r\n\f]*)*\047)'
|
||||||
|
nl_string2 = r'(?:"[^"\\\r\n\f]*(?:\\(?:[^\r]|\r\n?)[^"\\\r\n\f]*)*")'
|
||||||
|
nl_strings = r'(?:%s|%s)' % (nl_string1, nl_string2)
|
||||||
|
|
||||||
|
uri_nl_string1 = r'(?:\047[^\047\\]*(?:\\(?:[^\r]|\r\n?)[^\047\\]*)*\047)'
|
||||||
|
uri_nl_string2 = r'(?:"[^"\\]*(?:\\(?:[^\r]|\r\n?)[^"\\]*)*")'
|
||||||
|
uri_nl_strings = r'(?:%s|%s)' % (uri_nl_string1, uri_nl_string2)
|
||||||
|
|
||||||
|
nl_escaped = r'(?:\\%(nl)s)' % locals()
|
||||||
|
|
||||||
|
space = r'(?:%(spacechar)s|%(comment)s)' % locals()
|
||||||
|
|
||||||
|
ie7hack = r'(?:>/\*\*/)'
|
||||||
|
|
||||||
|
uri = (r'(?:'
|
||||||
|
r'(?:[^\000-\040"\047()\\\177]*'
|
||||||
|
r'(?:%(escape)s[^\000-\040"\047()\\\177]*)*)'
|
||||||
|
r'(?:'
|
||||||
|
r'(?:%(spacechar)s+|%(nl_escaped)s+)'
|
||||||
|
r'(?:'
|
||||||
|
r'(?:[^\000-\040"\047()\\\177]|%(escape)s|%(nl_escaped)s)'
|
||||||
|
r'[^\000-\040"\047()\\\177]*'
|
||||||
|
r'(?:%(escape)s[^\000-\040"\047()\\\177]*)*'
|
||||||
|
r')+'
|
||||||
|
r')*'
|
||||||
|
r')') % locals()
|
||||||
|
|
||||||
|
nl_unesc_sub = _re.compile(nl_escaped).sub
|
||||||
|
|
||||||
|
uri_space_sub = _re.compile((
|
||||||
|
r'(%(escape)s+)|%(spacechar)s+|%(nl_escaped)s+'
|
||||||
|
) % locals()).sub
|
||||||
|
uri_space_subber = lambda m: m.groups()[0] or ''
|
||||||
|
|
||||||
|
space_sub_simple = _re.compile((
|
||||||
|
r'[\r\n\f\040\t;]+|(%(comment)s+)'
|
||||||
|
) % locals()).sub
|
||||||
|
space_sub_banged = _re.compile((
|
||||||
|
r'[\r\n\f\040\t;]+|(%(_bang_comment)s+)'
|
||||||
|
) % locals()).sub
|
||||||
|
|
||||||
|
post_esc_sub = _re.compile(r'[\r\n\f\t]+').sub
|
||||||
|
|
||||||
|
main_sub = _re.compile((
|
||||||
|
r'([^\\"\047u>@\r\n\f\040\t/;:{}]+)'
|
||||||
|
r'|(?<=[{}(=:>+[,!])(%(space)s+)'
|
||||||
|
r'|^(%(space)s+)'
|
||||||
|
r'|(%(space)s+)(?=(([:{});=>+\],!])|$)?)'
|
||||||
|
r'|;(%(space)s*(?:;%(space)s*)*)(?=(\})?)'
|
||||||
|
r'|(\{)'
|
||||||
|
r'|(\})'
|
||||||
|
r'|(%(strings)s)'
|
||||||
|
r'|(?<!%(nmchar)s)url\(%(spacechar)s*('
|
||||||
|
r'%(uri_nl_strings)s'
|
||||||
|
r'|%(uri)s'
|
||||||
|
r')%(spacechar)s*\)'
|
||||||
|
r'|(@[mM][eE][dD][iI][aA])(?!%(nmchar)s)'
|
||||||
|
r'|(%(ie7hack)s)(%(space)s*)'
|
||||||
|
r'|(:[fF][iI][rR][sS][tT]-[lL]'
|
||||||
|
r'(?:[iI][nN][eE]|[eE][tT][tT][eE][rR]))'
|
||||||
|
r'(%(space)s*)(?=[{,])'
|
||||||
|
r'|(%(nl_strings)s)'
|
||||||
|
r'|(%(escape)s[^\\"\047u>@\r\n\f\040\t/;:{}]*)'
|
||||||
|
) % locals()).sub
|
||||||
|
|
||||||
|
#print main_sub.__self__.pattern
|
||||||
|
|
||||||
|
def main_subber(keep_bang_comments):
|
||||||
|
""" Make main subber """
|
||||||
|
in_macie5, in_rule, at_media = [0], [0], [0]
|
||||||
|
|
||||||
|
if keep_bang_comments:
|
||||||
|
space_sub = space_sub_banged
|
||||||
|
def space_subber(match):
|
||||||
|
""" Space|Comment subber """
|
||||||
|
if match.lastindex:
|
||||||
|
group1, group2 = match.group(1, 2)
|
||||||
|
if group2:
|
||||||
|
if group1.endswith(r'\*/'):
|
||||||
|
in_macie5[0] = 1
|
||||||
|
else:
|
||||||
|
in_macie5[0] = 0
|
||||||
|
return group1
|
||||||
|
elif group1:
|
||||||
|
if group1.endswith(r'\*/'):
|
||||||
|
if in_macie5[0]:
|
||||||
|
return ''
|
||||||
|
in_macie5[0] = 1
|
||||||
|
return r'/*\*/'
|
||||||
|
elif in_macie5[0]:
|
||||||
|
in_macie5[0] = 0
|
||||||
|
return '/**/'
|
||||||
|
return ''
|
||||||
|
else:
|
||||||
|
space_sub = space_sub_simple
|
||||||
|
def space_subber(match):
|
||||||
|
""" Space|Comment subber """
|
||||||
|
if match.lastindex:
|
||||||
|
if match.group(1).endswith(r'\*/'):
|
||||||
|
if in_macie5[0]:
|
||||||
|
return ''
|
||||||
|
in_macie5[0] = 1
|
||||||
|
return r'/*\*/'
|
||||||
|
elif in_macie5[0]:
|
||||||
|
in_macie5[0] = 0
|
||||||
|
return '/**/'
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def fn_space_post(group):
|
||||||
|
""" space with token after """
|
||||||
|
if group(5) is None or (
|
||||||
|
group(6) == ':' and not in_rule[0] and not at_media[0]):
|
||||||
|
return ' ' + space_sub(space_subber, group(4))
|
||||||
|
return space_sub(space_subber, group(4))
|
||||||
|
|
||||||
|
def fn_semicolon(group):
|
||||||
|
""" ; handler """
|
||||||
|
return ';' + space_sub(space_subber, group(7))
|
||||||
|
|
||||||
|
def fn_semicolon2(group):
|
||||||
|
""" ; handler """
|
||||||
|
if in_rule[0]:
|
||||||
|
return space_sub(space_subber, group(7))
|
||||||
|
return ';' + space_sub(space_subber, group(7))
|
||||||
|
|
||||||
|
def fn_open(group):
|
||||||
|
""" { handler """
|
||||||
|
# pylint: disable = W0613
|
||||||
|
if at_media[0]:
|
||||||
|
at_media[0] -= 1
|
||||||
|
else:
|
||||||
|
in_rule[0] = 1
|
||||||
|
return '{'
|
||||||
|
|
||||||
|
def fn_close(group):
|
||||||
|
""" } handler """
|
||||||
|
# pylint: disable = W0613
|
||||||
|
in_rule[0] = 0
|
||||||
|
return '}'
|
||||||
|
|
||||||
|
def fn_media(group):
|
||||||
|
""" @media handler """
|
||||||
|
at_media[0] += 1
|
||||||
|
return group(13)
|
||||||
|
|
||||||
|
def fn_ie7hack(group):
|
||||||
|
""" IE7 Hack handler """
|
||||||
|
if not in_rule[0] and not at_media[0]:
|
||||||
|
in_macie5[0] = 0
|
||||||
|
return group(14) + space_sub(space_subber, group(15))
|
||||||
|
return '>' + space_sub(space_subber, group(15))
|
||||||
|
|
||||||
|
table = (
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
fn_space_post, # space with token after
|
||||||
|
fn_space_post, # space with token after
|
||||||
|
fn_space_post, # space with token after
|
||||||
|
fn_semicolon, # semicolon
|
||||||
|
fn_semicolon2, # semicolon
|
||||||
|
fn_open, # {
|
||||||
|
fn_close, # }
|
||||||
|
lambda g: g(11), # string
|
||||||
|
lambda g: 'url(%s)' % uri_space_sub(uri_space_subber, g(12)),
|
||||||
|
# url(...)
|
||||||
|
fn_media, # @media
|
||||||
|
None,
|
||||||
|
fn_ie7hack, # ie7hack
|
||||||
|
None,
|
||||||
|
lambda g: g(16) + ' ' + space_sub(space_subber, g(17)),
|
||||||
|
# :first-line|letter followed
|
||||||
|
# by [{,] (apparently space
|
||||||
|
# needed for IE6)
|
||||||
|
lambda g: nl_unesc_sub('', g(18)), # nl_string
|
||||||
|
lambda g: post_esc_sub(' ', g(19)), # escape
|
||||||
|
)
|
||||||
|
|
||||||
|
def func(match):
|
||||||
|
""" Main subber """
|
||||||
|
idx, group = match.lastindex, match.group
|
||||||
|
if idx > 3:
|
||||||
|
return table[idx](group)
|
||||||
|
|
||||||
|
# shortcuts for frequent operations below:
|
||||||
|
elif idx == 1: # not interesting
|
||||||
|
return group(1)
|
||||||
|
#else: # space with token before or at the beginning
|
||||||
|
return space_sub(space_subber, group(idx))
|
||||||
|
|
||||||
|
return func
|
||||||
|
|
||||||
|
def cssmin(style, keep_bang_comments=False): # pylint: disable = W0621
|
||||||
|
"""
|
||||||
|
Minify CSS.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`style` : ``str``
|
||||||
|
CSS to minify
|
||||||
|
|
||||||
|
`keep_bang_comments` : ``bool``
|
||||||
|
Keep comments starting with an exclamation mark? (``/*!...*/``)
|
||||||
|
|
||||||
|
:Return: Minified style
|
||||||
|
:Rtype: ``str``
|
||||||
|
"""
|
||||||
|
return main_sub(main_subber(keep_bang_comments), style)
|
||||||
|
|
||||||
|
return cssmin
|
||||||
|
|
||||||
|
cssmin = _make_cssmin()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
def main():
|
||||||
|
""" Main """
|
||||||
|
import sys as _sys
|
||||||
|
keep_bang_comments = (
|
||||||
|
'-b' in _sys.argv[1:]
|
||||||
|
or '-bp' in _sys.argv[1:]
|
||||||
|
or '-pb' in _sys.argv[1:]
|
||||||
|
)
|
||||||
|
if '-p' in _sys.argv[1:] or '-bp' in _sys.argv[1:] \
|
||||||
|
or '-pb' in _sys.argv[1:]:
|
||||||
|
global cssmin # pylint: disable = W0603
|
||||||
|
cssmin = _make_cssmin(python_only=True)
|
||||||
|
_sys.stdout.write(cssmin(
|
||||||
|
_sys.stdin.read(), keep_bang_comments=keep_bang_comments
|
||||||
|
))
|
||||||
|
main()
|
42
setup.py
Normal file
42
setup.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: ascii -*-
|
||||||
|
#
|
||||||
|
# Copyright 2006 - 2013
|
||||||
|
# Andr\xe9 Malo or his licensors, as applicable
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import sys as _sys
|
||||||
|
from _setup import run
|
||||||
|
|
||||||
|
|
||||||
|
def setup(args=None, _manifest=0):
|
||||||
|
""" Main setup function """
|
||||||
|
from _setup.ext import Extension
|
||||||
|
|
||||||
|
if 'java' in _sys.platform.lower():
|
||||||
|
# no c extension for jython
|
||||||
|
ext = None
|
||||||
|
else:
|
||||||
|
ext=[Extension('_rcssmin', sources=['rcssmin.c'])]
|
||||||
|
|
||||||
|
return run(script_args=args, ext=ext, manifest_only=_manifest)
|
||||||
|
|
||||||
|
|
||||||
|
def manifest():
|
||||||
|
""" Create List of packaged files """
|
||||||
|
return setup((), _manifest=1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
setup()
|
1
tests/main/comment_00.css
Normal file
1
tests/main/comment_00.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
/* this is a comment */i {love: comments; /*! yes */; /*YES*/}
|
7
tests/main/comment_01.css
Normal file
7
tests/main/comment_01.css
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#mainnav li.hover dl.subsearch select {
|
||||||
|
margin-top /*\**/:4px\9;
|
||||||
|
margin-bottom /*\**/:0px\9;
|
||||||
|
}
|
||||||
|
#mainnav li.hover dl.subsearch label {
|
||||||
|
margin-top /*\**/:4px\9;
|
||||||
|
}
|
1
tests/main/comment_02.css
Normal file
1
tests/main/comment_02.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
/*/
|
1
tests/main/comment_03.css
Normal file
1
tests/main/comment_03.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
a/***/b
|
1
tests/main/comment_04.css
Normal file
1
tests/main/comment_04.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
a/**\/*/b
|
1
tests/main/escape_00.css
Normal file
1
tests/main/escape_00.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
\\0
|
1
tests/main/escape_01.css
Normal file
1
tests/main/escape_01.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
\0
|
1
tests/main/escape_02.css
Normal file
1
tests/main/escape_02.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
\10
|
1
tests/main/escape_03.css
Normal file
1
tests/main/escape_03.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
\0345
|
1
tests/main/escape_04.css
Normal file
1
tests/main/escape_04.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
\01234567
|
1
tests/main/escape_05.css
Normal file
1
tests/main/escape_05.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
\012345 la
|
1
tests/main/escape_06.css
Normal file
1
tests/main/escape_06.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
\a bc
|
1
tests/main/first_00.css
Normal file
1
tests/main/first_00.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
x:first-line{bla: blub;}
|
1
tests/main/first_01.css
Normal file
1
tests/main/first_01.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
x:first-letter{bla: blub;}
|
1
tests/main/first_02.css
Normal file
1
tests/main/first_02.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
x:first-letter{bla:blub}y:first-line{foo:bar}
|
1
tests/main/out/comment_00.out
Normal file
1
tests/main/out/comment_00.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
i{love:comments}
|
1
tests/main/out/comment_00.out.b
Normal file
1
tests/main/out/comment_00.out.b
Normal file
@ -0,0 +1 @@
|
|||||||
|
i{love:comments/*! yes */}
|
1
tests/main/out/comment_01.out
Normal file
1
tests/main/out/comment_01.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
#mainnav li.hover dl.subsearch select{margin-top:4px\9;margin-bottom:0px\9}#mainnav li.hover dl.subsearch label{margin-top:4px\9}
|
1
tests/main/out/comment_01.out.b
Normal file
1
tests/main/out/comment_01.out.b
Normal file
@ -0,0 +1 @@
|
|||||||
|
#mainnav li.hover dl.subsearch select{margin-top:4px\9;margin-bottom:0px\9}#mainnav li.hover dl.subsearch label{margin-top:4px\9}
|
1
tests/main/out/comment_02.out
Normal file
1
tests/main/out/comment_02.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
/*/
|
1
tests/main/out/comment_02.out.b
Normal file
1
tests/main/out/comment_02.out.b
Normal file
@ -0,0 +1 @@
|
|||||||
|
/*/
|
1
tests/main/out/comment_03.out
Normal file
1
tests/main/out/comment_03.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
a b
|
1
tests/main/out/comment_03.out.b
Normal file
1
tests/main/out/comment_03.out.b
Normal file
@ -0,0 +1 @@
|
|||||||
|
a b
|
1
tests/main/out/comment_04.out
Normal file
1
tests/main/out/comment_04.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
a b
|
1
tests/main/out/comment_04.out.b
Normal file
1
tests/main/out/comment_04.out.b
Normal file
@ -0,0 +1 @@
|
|||||||
|
a b
|
1
tests/main/out/escape_00.out
Normal file
1
tests/main/out/escape_00.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
\\0
|
1
tests/main/out/escape_00.out.b
Normal file
1
tests/main/out/escape_00.out.b
Normal file
@ -0,0 +1 @@
|
|||||||
|
\\0
|
1
tests/main/out/escape_01.out
Normal file
1
tests/main/out/escape_01.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
\0
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user