import rcssmin

This commit is contained in:
ndparker 2014-02-09 13:34:45 +01:00
parent c5cf1641ea
commit 0a77228dc5
332 changed files with 15808 additions and 35 deletions

39
.gitignore vendored
View File

@ -1,36 +1,13 @@
Makefile
*.py[cod]
# C extensions
*.so
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
lib
lib64
__pycache__
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
nosetests.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
docs/_userdoc/_build/
docs/apidoc/
docs/userdoc/
_website/
*.ebuild
.*.sw?
MANIFEST

201
LICENSE Normal file
View 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.

View File

@ -1,4 +0,0 @@
rcssmin
=======
Fast CSS minifier for Python

152
README.rst Normal file
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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)

View 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
View 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

View 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
View 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
View 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
View 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
View 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)

View 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
View 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()

View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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)

View 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
View 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

View 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
View 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
View 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
View 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
View 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)

View 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
View 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()

View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View File

225
bench/cssmin.py Normal file
View 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

File diff suppressed because it is too large Load Diff

1
bench/wikipedia.min.css vendored Normal file

File diff suppressed because one or more lines are too long

5
compile-dev Executable file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1 @@
rcssmin (1.0)

1
docs/SUMMARY Normal file
View File

@ -0,0 +1 @@
CSS Minifier

View File

@ -0,0 +1,5 @@
.ci {
font-variant: small-caps;
font-family: serif;
font-style: normal;
}

View File

@ -0,0 +1,2 @@
{% extends "default/layout.html" %}
{% set css_files = css_files + ['_static/ci.css'] %}

228
docs/_userdoc/conf.py Normal file
View 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

View 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
View 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

View 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

View 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
View 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
View 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
View 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
View 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

1099
rcssmin.c Normal file

File diff suppressed because it is too large Load Diff

360
rcssmin.py Executable file
View 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
View 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()

View File

@ -0,0 +1 @@
/* this is a comment */i {love: comments; /*! yes */; /*YES*/}

View 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;
}

View File

@ -0,0 +1 @@
/*/

View File

@ -0,0 +1 @@
a/***/b

View File

@ -0,0 +1 @@
a/**\/*/b

1
tests/main/escape_00.css Normal file
View File

@ -0,0 +1 @@
\\0

1
tests/main/escape_01.css Normal file
View File

@ -0,0 +1 @@
\0

1
tests/main/escape_02.css Normal file
View File

@ -0,0 +1 @@
\10

1
tests/main/escape_03.css Normal file
View File

@ -0,0 +1 @@
\0345

1
tests/main/escape_04.css Normal file
View File

@ -0,0 +1 @@
\01234567

1
tests/main/escape_05.css Normal file
View File

@ -0,0 +1 @@
\012345 la

1
tests/main/escape_06.css Normal file
View File

@ -0,0 +1 @@
\a bc

1
tests/main/first_00.css Normal file
View File

@ -0,0 +1 @@
x:first-line{bla: blub;}

1
tests/main/first_01.css Normal file
View File

@ -0,0 +1 @@
x:first-letter{bla: blub;}

1
tests/main/first_02.css Normal file
View File

@ -0,0 +1 @@
x:first-letter{bla:blub}y:first-line{foo:bar}

View File

@ -0,0 +1 @@
i{love:comments}

View File

@ -0,0 +1 @@
i{love:comments/*! yes */}

View 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}

View 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}

View File

@ -0,0 +1 @@
/*/

View File

@ -0,0 +1 @@
/*/

View File

@ -0,0 +1 @@
a b

View File

@ -0,0 +1 @@
a b

View File

@ -0,0 +1 @@
a b

View File

@ -0,0 +1 @@
a b

View File

@ -0,0 +1 @@
\\0

View File

@ -0,0 +1 @@
\\0

View File

@ -0,0 +1 @@
\0

Some files were not shown because too many files have changed in this diff Show More