├── test
└── example
│ ├── __init__.py
│ ├── urls.py
│ ├── media
│ └── css
│ │ └── 960
│ │ ├── reset.css
│ │ ├── text.css
│ │ ├── grid-24.css
│ │ └── grid.css
│ └── settings.py
├── REQUIREMENTS
├── MANIFEST.in
├── src
└── djcssmin
│ ├── __init__.py
│ ├── utils.py
│ └── commands.py
├── .gitignore
├── doc
├── .markdoc.yaml
├── settings.md
└── index.md
├── setup.py
├── UNLICENSE
├── README.md
└── distribute_setup.py
/test/example/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/REQUIREMENTS:
--------------------------------------------------------------------------------
1 | django-boss>=0.3
2 | cssmin>=0.1
3 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include REQUIREMENTS
2 | include distribute_setup.py
3 |
--------------------------------------------------------------------------------
/src/djcssmin/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | __version__ = '0.3'
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.egg-info
2 | *.pyc
3 | *.pyo
4 | .DS_Store
5 | MANIFEST
6 | build
7 | dist
8 | doc/.html
9 | doc/.tmp
10 | test/example/dev.db
11 | test/example/media/css/*.min.css
12 |
--------------------------------------------------------------------------------
/doc/.markdoc.yaml:
--------------------------------------------------------------------------------
1 | wiki-name: django-cssmin Documentation
2 |
3 | static-dir: ".static"
4 | wiki-dir: "."
5 |
6 | markdown:
7 | extensions:
8 | - codehilite
9 | - def_list
10 | - headerid
11 |
12 |
--------------------------------------------------------------------------------
/test/example/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls.defaults import *
2 |
3 | # Uncomment the next two lines to enable the admin:
4 | # from django.contrib import admin
5 | # admin.autodiscover()
6 |
7 | urlpatterns = patterns('',
8 | # Example:
9 | # (r'^example/', include('example.foo.urls')),
10 |
11 | # Uncomment the admin/doc line below and add 'django.contrib.admindocs'
12 | # to INSTALLED_APPS to enable admin documentation:
13 | # (r'^admin/doc/', include('django.contrib.admindocs.urls')),
14 |
15 | # Uncomment the next line to enable the admin:
16 | # (r'^admin/', include(admin.site.urls)),
17 | )
18 |
--------------------------------------------------------------------------------
/test/example/media/css/960/reset.css:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/ */
2 | /* v1.0 | 20080212 */
3 |
4 | html, body, div, span, applet, object, iframe,
5 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
6 | a, abbr, acronym, address, big, cite, code,
7 | del, dfn, em, font, img, ins, kbd, q, s, samp,
8 | small, strike, strong, sub, sup, tt, var,
9 | b, u, i, center,
10 | dl, dt, dd, ol, ul, li,
11 | fieldset, form, label, legend,
12 | table, caption, tbody, tfoot, thead, tr, th, td {
13 | margin: 0;
14 | padding: 0;
15 | border: 0;
16 | outline: 0;
17 | font-size: 100%;
18 | vertical-align: baseline;
19 | background: transparent;
20 | }
21 | body {
22 | line-height: 1;
23 | }
24 | ol, ul {
25 | list-style: none;
26 | }
27 | blockquote, q {
28 | quotes: none;
29 | }
30 | blockquote:before, blockquote:after,
31 | q:before, q:after {
32 | content: '';
33 | content: none;
34 | }
35 |
36 | /* remember to define focus styles! */
37 | :focus {
38 | outline: 0;
39 | }
40 |
41 | /* remember to highlight inserts somehow! */
42 | ins {
43 | text-decoration: none;
44 | }
45 | del {
46 | text-decoration: line-through;
47 | }
48 |
49 | /* tables still need 'cellspacing="0"' in the markup */
50 | table {
51 | border-collapse: collapse;
52 | border-spacing: 0;
53 | }
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import os
5 | import re
6 |
7 | from distribute_setup import use_setuptools; use_setuptools()
8 | from setuptools import setup, find_packages
9 |
10 |
11 | rel_file = lambda *args: os.path.join(os.path.dirname(os.path.abspath(__file__)), *args)
12 |
13 | def read_from(filename):
14 | fp = open(filename)
15 | try:
16 | return fp.read()
17 | finally:
18 | fp.close()
19 |
20 | def get_version():
21 | data = read_from(rel_file('src', 'djcssmin', '__init__.py'))
22 | return re.search(r"__version__ = '([^']+)'", data).group(1)
23 |
24 | def get_requirements():
25 | data = read_from(rel_file('REQUIREMENTS'))
26 | lines = map(lambda s: s.strip(), data.splitlines())
27 | return filter(None, lines)
28 |
29 |
30 | setup(
31 | name = 'django-cssmin',
32 | version = get_version(),
33 | author = "Zachary Voase",
34 | author_email = "zacharyvoase@me.com",
35 | url = 'http://github.com/zacharyvoase/django-cssmin',
36 | description = "CSS compression for Django.",
37 | packages = find_packages(where='src'),
38 | package_dir = {'': 'src'},
39 | install_requires = get_requirements(),
40 | )
41 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
--------------------------------------------------------------------------------
/test/example/media/css/960/text.css:
--------------------------------------------------------------------------------
1 | /*
2 | 960 Grid System ~ Text CSS.
3 | Learn more ~ http://960.gs/
4 |
5 | Licensed under GPL and MIT.
6 | */
7 |
8 | /* `Basic HTML
9 | ----------------------------------------------------------------------------------------------------*/
10 |
11 | body {
12 | font: 13px/1.5 'Helvetica Neue', Arial, 'Liberation Sans', FreeSans, sans-serif;
13 | }
14 |
15 | a:focus {
16 | outline: 1px dotted;
17 | }
18 |
19 | hr {
20 | border: 0 #ccc solid;
21 | border-top-width: 1px;
22 | clear: both;
23 | height: 0;
24 | }
25 |
26 | /* `Headings
27 | ----------------------------------------------------------------------------------------------------*/
28 |
29 | h1 {
30 | font-size: 25px;
31 | }
32 |
33 | h2 {
34 | font-size: 23px;
35 | }
36 |
37 | h3 {
38 | font-size: 21px;
39 | }
40 |
41 | h4 {
42 | font-size: 19px;
43 | }
44 |
45 | h5 {
46 | font-size: 17px;
47 | }
48 |
49 | h6 {
50 | font-size: 15px;
51 | }
52 |
53 | /* `Spacing
54 | ----------------------------------------------------------------------------------------------------*/
55 |
56 | ol {
57 | list-style: decimal;
58 | }
59 |
60 | ul {
61 | list-style: disc;
62 | }
63 |
64 | li {
65 | margin-left: 30px;
66 | }
67 |
68 | p,
69 | dl,
70 | hr,
71 | h1,
72 | h2,
73 | h3,
74 | h4,
75 | h5,
76 | h6,
77 | ol,
78 | ul,
79 | pre,
80 | table,
81 | address,
82 | fieldset {
83 | margin-bottom: 20px;
84 | }
--------------------------------------------------------------------------------
/test/example/settings.py:
--------------------------------------------------------------------------------
1 | # Django settings for example project.
2 |
3 | DEBUG = True
4 | TEMPLATE_DEBUG = DEBUG
5 |
6 | ADMINS = (
7 | ('Zachary Voase', 'zacharyvoase@me.com'),
8 | )
9 |
10 | MANAGERS = ADMINS
11 |
12 | DATABASE_ENGINE = 'sqlite3'
13 | DATABASE_NAME = 'dev.db'
14 | DATABASE_USER = ''
15 | DATABASE_PASSWORD = ''
16 | DATABASE_HOST = ''
17 | DATABASE_PORT = ''
18 | TIME_ZONE = 'America/Chicago'
19 | LANGUAGE_CODE = 'en-us'
20 | SITE_ID = 1
21 | USE_I18N = True
22 | MEDIA_ROOT = ''
23 | MEDIA_URL = ''
24 | ADMIN_MEDIA_PREFIX = '/media/'
25 | SECRET_KEY = '1(lavq&ib1(a+=_%(6_)njg0^y$i*2t@e3#0wl1k)zehg*$nf$'
26 | TEMPLATE_LOADERS = (
27 | 'django.template.loaders.filesystem.load_template_source',
28 | 'django.template.loaders.app_directories.load_template_source',
29 | # 'django.template.loaders.eggs.load_template_source',
30 | )
31 | MIDDLEWARE_CLASSES = (
32 | 'django.middleware.common.CommonMiddleware',
33 | 'django.contrib.sessions.middleware.SessionMiddleware',
34 | )
35 | ROOT_URLCONF = 'example.urls'
36 | TEMPLATE_DIRS = (
37 | )
38 |
39 | INSTALLED_APPS = (
40 | 'django.contrib.contenttypes',
41 | 'django.contrib.sessions',
42 | 'django.contrib.sites',
43 | 'djcssmin',
44 | )
45 |
46 | CSSMIN_INPUT = [
47 | # YUI Reset CSS
48 | 'http://yui.yahooapis.com/2.8.0r4/build/reset/reset.css',
49 | ## 960 Grid System
50 | 'media/css/960/reset.css',
51 | 'media/css/960/text.css',
52 | 'media/css/960/grid.css',
53 | # Local CSS Libraries
54 | 'media/css/*.lib.css',
55 | ]
56 |
57 | CSSMIN_OUTPUT = 'media/css/style.min.css'
58 |
59 |
60 | import logging
61 |
62 | logging.root.setLevel(logging.DEBUG)
63 | handler = logging.StreamHandler()
64 | handler.setLevel(logging.DEBUG)
65 | logging.root.addHandler(handler)
66 |
--------------------------------------------------------------------------------
/doc/settings.md:
--------------------------------------------------------------------------------
1 | # Settings
2 |
3 | `django-cssmin` will determine how to compress your CSS files based on the
4 | following settings.
5 |
6 | `DEBUG`
7 | : If this is `True`, `django-cssmin` will not (by default) compress the output
8 | CSS. This is to help with interactive debugging during design. It can be
9 | overridden with the options to the command-line interface; see the output of
10 | `djboss cssmin --help` for more information.
11 |
12 | `CSSMIN_INPUT` (required)
13 | : A list of [glob][] patterns or URLs which `django-cssmin` will expand to get
14 | a list of CSS stylesheet filenames. For example:
15 |
16 | [glob]: http://docs.python.org/library/glob.html
17 |
18 | CSSMIN_INPUT = [
19 | # YUI Reset CSS
20 | 'http://yui.yahooapis.com/2.8.0r4/build/reset/reset.css',
21 | ## 960 Grid System
22 | 'media/css/960/reset.css',
23 | 'media/css/960/text.css',
24 | 'media/css/960/grid.css',
25 | # Local CSS Libraries
26 | 'media/css/*.lib.css',
27 | ]
28 |
29 | First, the YUI CSS Reset library is downloaded from Yahoo’s servers. The
30 | specified CSS files for the 960.gs framework are included in the given
31 | order. Then, all CSS files in the `media/css/` directory ending in
32 | `.lib.css` are included. Globs which do not match anything just resolve to
33 | empty lists.
34 |
35 | All relative paths in globs are first resolved. `django-cssmin` will check
36 | for the following settings, in this order:
37 |
38 | * `CSSMIN_ROOT`
39 | * `PROJECT_DIR`
40 | * `PROJECT_ROOT`
41 |
42 | Finally, if none exist, the directory containing the settings module will be
43 | considered the base path for resolution.
44 |
45 | `CSSMIN_OUTPUT` (required)
46 | : The filename to which the compressed CSS style data will be written.
47 | Relative output filenames will be resolved as specified above.
48 |
49 | `CSSMIN_ROOT` (optional)
50 | : See the paragraph on relative glob resolution above.
51 |
52 | `CSSMIN_PROLOG` (optional)
53 | : A filename (relative or absolute) which contains a piece of text to include
54 | at the beginning of the compressed CSS stylesheet. This will usually be a
55 | comment containing a copyright statement or license information.
56 |
--------------------------------------------------------------------------------
/src/djcssmin/utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import logging
4 | import os
5 | import subprocess
6 | import tempfile
7 | import urllib2
8 | import urlparse
9 |
10 |
11 | LOG = logging.getLogger('django.djcssmin')
12 |
13 |
14 | ## workaround for Python pre-2.6.
15 | if not hasattr(os.path, 'relpath'):
16 | def relpath(path, start=os.path.curdir):
17 | """Return a relative version of a path"""
18 |
19 | if not path:
20 | raise ValueError("no path specified")
21 |
22 | start_list = os.path.abspath(start).split(os.path.sep)
23 | path_list = os.path.abspath(path).split(os.path.sep)
24 |
25 | # Work out how much of the filepath is shared by start and path.
26 | i = len(os.path.commonprefix([start_list, path_list]))
27 |
28 | rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
29 | if not rel_list:
30 | return os.path.curdir
31 | return os.path.join(*rel_list)
32 | os.path.relpath = relpath
33 |
34 |
35 | def get_root(settings):
36 | """Get the directory from which to resolve the `CSSMIN_INPUT` globs."""
37 |
38 | if hasattr(settings, 'CSSMIN_ROOT'):
39 | return settings.CSSMIN_ROOT
40 | elif hasattr(settings, 'PROJECT_DIR'):
41 | return settings.PROJECT_DIR
42 | elif hasattr(settings, 'PROJECT_ROOT'):
43 | return settings.PROJECT_ROOT
44 | return os.path.dirname(os.path.abspath(settings.__file__))
45 |
46 |
47 | def get_prolog(settings, root):
48 | """Get the prolog data from the `CSSMIN_PROLOG` setting."""
49 |
50 | if hasattr(settings, 'CSSMIN_PROLOG'):
51 | filename = make_abs(settings.CSSMIN_PROLOG, root)
52 | if not os.path.exists(filename):
53 | LOG.warn("Specified CSSMIN_PROLOG does not exist, continuing anyway")
54 | else:
55 | return read_from(filename)
56 | return ''
57 |
58 |
59 | def make_abs(path, root):
60 | """Ensure a path is absolute."""
61 |
62 | return path if os.path.isabs(path) else os.path.abspath(os.path.join(root, path))
63 |
64 |
65 | def temp_fetch(url):
66 | """Fetch a URL and save it in a temporary file, returning the filename."""
67 |
68 | conn = urllib2.urlopen(url)
69 | try:
70 | fp = tempfile.NamedTemporaryFile(delete=False)
71 | LOG.info("Saving %s to a temporary file" % truncate_url(url))
72 | try:
73 | fp.write(conn.read())
74 | finally:
75 | fp.close()
76 | finally:
77 | conn.close()
78 |
79 | LOG.info("Saved %s to %s" % (truncate_url(url), os.path.basename(fp.name)))
80 | return fp.name
81 |
82 |
83 | def truncate_url(url):
84 | """Return a short version of a URL."""
85 |
86 | split = list(urlparse.urlsplit(url))
87 |
88 | path = split[2]
89 | if path == '/' or path.count('/') <= 2:
90 | pass
91 | elif path.endswith('/'):
92 | split[2] = '/.../' + '/'.join(path.rsplit('/', 2)[-2:])
93 | else:
94 | split[2] = '/.../' + path.rsplit('/', 1)[-1]
95 |
96 | return urlparse.urlunsplit(split)
97 |
98 |
99 | def read_from(filename):
100 | fp = open(filename)
101 | try:
102 | return fp.read()
103 | finally:
104 | fp.close()
105 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # `django-cssmin`
2 |
3 | `django-cssmin` is a reusable application for [Django][] which makes it easy to
4 | automatically [compress][] your CSS stylesheets.
5 |
6 | [django]: http://www.djangoproject.com/
7 | [compress]: http://developer.yahoo.com/yui/compressor/
8 |
9 |
10 | ## Why compress?
11 |
12 | * Reduce the overhead of multiple HTTP requests for multiple CSS files. By
13 | concatenating them into one file, the browser will only need to make one
14 | HTTP request, reducing server load and bandwidth.
15 |
16 | * Reduce the size of CSS files by stripping unnecessary bytes (whitespace, et
17 | cetera), saving network bandwidth and reducing page load times.
18 |
19 | ## Installation and Setup
20 |
21 | * Install the `django-cssmin` library:
22 |
23 | $ pip install django-cssmin # OR
24 | $ easy_install django-cssmin
25 |
26 | Either of these commands should install all the required dependencies.
27 |
28 | * Add `'djcssmin'` to your `INSTALLED_APPS` setting.
29 |
30 | * Add the necessary settings to your `settings.py` file:
31 |
32 | ## somewhere in settings.py
33 | CSSMIN_ROOT = '/path/to/my/project/'
34 |
35 | CSSMIN_INPUT = [
36 | # YUI Reset CSS
37 | 'http://yui.yahooapis.com/2.8.0r4/build/reset/reset.css',
38 | # 960 Grid System
39 | 'media/css/960/reset.css',
40 | 'media/css/960/text.css',
41 | 'media/css/960/grid.css',
42 | # Local CSS Libraries
43 | 'media/css/*.lib.css',
44 | ]
45 |
46 | CSSMIN_OUTPUT = 'media/css/style.min.css'
47 |
48 | More information on the available settings can be found
49 | [here](http://github.com/zacharyvoase/django-cssmin/blob/master/doc/settings.md).
50 |
51 |
52 | ## `DEBUG` mode
53 |
54 | If `DEBUG` is set to `True` in your Django project when you run `django-cssmin`,
55 | the CSS input files will only be concatenated to the output file, not
56 | compressed. This allows you to debug your stylesheets with meaningful line
57 | numbers during design, and then use the fully-minified version in production.
58 | You can force specific behaviour with options to the `djboss cssmin` command;
59 | see the output of `djboss cssmin --help` for more information.
60 |
61 |
62 | ## Usage
63 |
64 | `django-cssmin` uses [`django-boss`][djboss], a library/tool for writing and
65 | running Django management commands. This will be installed automatically by
66 | setuptools when you install `django-cssmin`.
67 |
68 | [djboss]: http://github.com/zacharyvoase/django-boss
69 |
70 | Usage is relatively simple:
71 |
72 | $ djboss --log-level DEBUG cssmin --prod-mode
73 | Saving http://yui.yahooapis.com/.../reset.css to a temporary file
74 | Saved http://yui.yahooapis.com/.../reset.css to tmp8QRfOa
75 | Reading tmp8QRfOa
76 | Reading media/css/960/reset.css
77 | Reading media/css/960/text.css
78 | Reading media/css/960/grid.css
79 | Minifying and writing to media/css/style.min.css
80 | Cleaning temporary file tmp8QRfOa
81 |
82 | The compressed CSS stylesheet will be output to the filename given by the
83 | `CSSMIN_OUTPUT` setting, in this case `media/css/style.min.css`.
84 |
85 |
86 | ## (Un)license
87 |
88 | This is free and unencumbered software released into the public domain.
89 |
90 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
91 | software, either in source code form or as a compiled binary, for any purpose,
92 | commercial or non-commercial, and by any means.
93 |
94 | In jurisdictions that recognize copyright laws, the author or authors of this
95 | software dedicate any and all copyright interest in the software to the public
96 | domain. We make this dedication for the benefit of the public at large and to
97 | the detriment of our heirs and successors. We intend this dedication to be an
98 | overt act of relinquishment in perpetuity of all present and future rights to
99 | this software under copyright law.
100 |
101 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
102 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
103 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE
104 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
105 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
106 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
107 |
108 | For more information, please refer to
109 |
--------------------------------------------------------------------------------
/src/djcssmin/commands.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from cStringIO import StringIO
4 | import glob
5 | import logging
6 | import os
7 | import os.path as p
8 |
9 | import cssmin as libcssmin
10 | from django.core.exceptions import ImproperlyConfigured
11 | from djboss.commands import *
12 |
13 | from djcssmin import utils
14 |
15 |
16 | LOG = logging.getLogger('django.djcssmin')
17 |
18 |
19 | def resolve_patterns(patterns, root):
20 | """Resolve a list of globs/URLs into absolute filenames."""
21 |
22 | input_files, temp_files = [], []
23 |
24 | for pattern in patterns:
25 | # Handle URLs in the CSSMIN_INPUT setting.
26 | if pattern.startswith("http:"):
27 | temp_filename = utils.temp_fetch(pattern)
28 | input_files.append(temp_filename)
29 | temp_files.append(temp_filename)
30 |
31 | else:
32 | # Ensure glob patterns are absolute.
33 | glob_files = glob.glob(utils.make_abs(pattern, root))
34 | # Sort filenames within the results of a single pattern.
35 | glob_files.sort()
36 |
37 | for filename in glob_files:
38 | # Make sure there are no repetitions.
39 | if filename not in input_files:
40 | input_files.append(filename)
41 |
42 | return input_files, temp_files
43 |
44 |
45 | @command
46 | @argument('-d', '--dev-mode', action='store_true', default=None, dest='development_mode',
47 | help="Don't minify (just concatenate). Defaults to the value of DEBUG.")
48 | @argument('-p', '--prod-mode', action='store_false', dest='development_mode',
49 | help="Compress, even when DEBUG is True.")
50 | def cssmin(args):
51 | """Compress the configured CSS stylesheets."""
52 |
53 | if not hasattr(args.settings, 'CSSMIN_INPUT'):
54 | raise ImproperlyConfigured("Must provide a CSSMIN_INPUT setting")
55 | elif not hasattr(args.settings, 'CSSMIN_OUTPUT'):
56 | raise ImproperlyConfigured("Must provide a CSSMIN_OUTPUT setting")
57 |
58 | root = utils.get_root(args.settings)
59 |
60 | # Set up development mode. If nothing is specified, this will default to the
61 | # value of `settings.DEBUG`. The `-d` and `-p` options override this value.
62 | if args.development_mode is None:
63 | development_mode = args.settings.DEBUG
64 | else:
65 | development_mode = args.development_mode
66 |
67 | # `temp_files` have to be deleted after processing, whether minification was
68 | # successful or not.
69 | input_files, temp_files = resolve_patterns(args.settings.CSSMIN_INPUT, root)
70 |
71 | try:
72 | # Get an absolute output filename.
73 | output_file = utils.make_abs(args.settings.CSSMIN_OUTPUT, root)
74 |
75 | if output_file in input_files:
76 | # This can happen if you output a '.css' file to the same directory
77 | # you're globbing from. Remove it from the input files.
78 | input_files.remove(output_file)
79 |
80 | input_io = StringIO()
81 | try:
82 | # Populate the input StringIO.
83 | for filename in input_files:
84 | if filename in temp_files:
85 | LOG.info("Reading %s" % p.basename(filename))
86 | else:
87 | LOG.info("Reading %s" % p.relpath(filename))
88 |
89 | # The additional whitespace/comments will be filtered out by the
90 | # compressor later on, unless we are in development mode, in
91 | # which case we want the whitespace and comments.
92 | input_io.write("/* FILE: %s */" % filename + (os.linesep * 2))
93 | input_io.write(utils.read_from(filename))
94 | input_io.write(os.linesep * 2)
95 | input_io.seek(0)
96 |
97 | output_io = open(output_file, 'w')
98 | try:
99 | output_io.write(utils.get_prolog(args.settings, root))
100 |
101 | if development_mode:
102 | LOG.info("Writing to %s" % p.relpath(output_file))
103 | output_io.write(input_io.getvalue())
104 | else:
105 | # Minify and write the output.
106 | LOG.info("Minifying and writing to %s" % p.relpath(output_file))
107 | output_io.write(libcssmin.cssmin(input_io.read()))
108 | finally:
109 | output_io.close() # Clean up.
110 | finally:
111 | input_io.close() # Clean up.
112 |
113 | finally:
114 | # Clean up.
115 | for temp_filename in temp_files:
116 | LOG.info("Cleaning temporary file %s" % p.basename(temp_filename))
117 | os.remove(temp_filename)
118 |
--------------------------------------------------------------------------------
/doc/index.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # `django-cssmin`
4 |
5 | `django-cssmin` is a reusable application for [Django][] which makes it easy to
6 | automatically [compress][] your CSS stylesheets.
7 |
8 | [django]: http://www.djangoproject.com/
9 | [compress]: http://developer.yahoo.com/yui/compressor/
10 |
11 |
12 | ## Why compress?
13 |
14 | * Reduce the overhead of multiple HTTP requests for multiple CSS files. By
15 | concatenating them into one file, the browser will only need to make one
16 | HTTP request, reducing server load and bandwidth.
17 |
18 | * Reduce the size of CSS files by stripping unnecessary bytes (whitespace, et
19 | cetera), saving network bandwidth and reducing page load times.
20 |
21 | ## Installation and Setup
22 |
23 | * Install the `django-cssmin` library:
24 |
25 | :::bash
26 | $ pip install django-cssmin # OR
27 | $ easy_install django-cssmin
28 |
29 | Either of these commands should install all the required dependencies. The
30 | YUI CSS compressor is installed via the [yuicompressor][] PyPI package; one
31 | way or another, `django-cssmin` expects a `yuicompressor` command to be
32 | available. If the PyPI package doesn’t install correctly for you, see
33 | [installing the YUI compressor](#installing-the-yui-compressor).
34 |
35 | [yuicompressor]: http://pypi.python.org/pypi/yuicompressor
36 |
37 | * Add `'djcssmin'` to your `INSTALLED_APPS` setting.
38 |
39 | * Add the necessary settings to your `settings.py` file:
40 |
41 | :::python
42 | ## somewhere in settings.py
43 | CSSMIN_ROOT = '/path/to/my/project/'
44 |
45 | CSSMIN_INPUT = [
46 | # YUI Reset CSS
47 | 'http://yui.yahooapis.com/2.8.0r4/build/reset/reset.css',
48 | # 960 Grid System
49 | 'media/css/960/reset.css',
50 | 'media/css/960/text.css',
51 | 'media/css/960/grid.css',
52 | # Local CSS Libraries
53 | 'media/css/*.lib.css',
54 | ]
55 |
56 | CSSMIN_OUTPUT = 'media/css/style.min.css'
57 |
58 | More information on the available settings can be found [here](/settings).
59 |
60 |
61 | ## `DEBUG` mode
62 |
63 | If `DEBUG` is set to `True` in your Django project when you run `django-cssmin`,
64 | the CSS input files will only be concatenated to the output file, not
65 | compressed. This allows you to debug your stylesheets with meaningful line
66 | numbers during design, and then use the fully-minified version in production.
67 | You can force specific behaviour with options to the `djboss cssmin` command;
68 | see the output of `djboss cssmin --help` for more information.
69 |
70 |
71 | ## Usage
72 |
73 | `django-cssmin` uses [`django-boss`][djboss], a library/tool for writing and
74 | running Django management commands. This will be installed automatically by
75 | setuptools when you install `django-cssmin`.
76 |
77 | [djboss]: http://github.com/zacharyvoase/django-boss
78 |
79 | Usage is relatively simple:
80 |
81 | :::bash
82 | $ djboss --log-level DEBUG cssmin --prod-mode
83 | Saving http://yui.yahooapis.com/.../reset.css to a temporary file
84 | Saved http://yui.yahooapis.com/.../reset.css to tmp8QRfOa
85 | Reading tmp8QRfOa
86 | Reading media/css/960/reset.css
87 | Reading media/css/960/text.css
88 | Reading media/css/960/grid.css
89 | Minifying and writing to media/css/style.min.css
90 | Cleaning temporary file tmp8QRfOa
91 |
92 | The compressed CSS stylesheet will be output to the filename given by the
93 | `CSSMIN_OUTPUT` setting, in this case `media/css/style.min.css`.
94 |
95 |
96 | ## Installing the YUI Compressor
97 |
98 | * Download the yuicompressor JAR file from
99 | [here](http://yuilibrary.com/downloads/#yuicompressor).
100 |
101 | * Extract the `yuicompressor-x.y.z.jar` file to a path in your `JAVA_HOME`
102 | environment variable.
103 |
104 | * Make `yuicompressor` an alias for `java -jar yuicompressor-x.y.z.jar`,
105 | using a proxy `yuicompressor` script on your path:
106 |
107 | #!/bin/bash
108 | # For example, in /usr/bin/yuicompressor:
109 | java -jar yuicompressor-x.y.z.jar $*
110 |
111 | Ensure this file has the executable bit set (via
112 | `sudo chmod a+x /usr/bin/yuicompressor` or otherwise).
113 |
114 | * Test the installation by running `yuicompressor --help`; you should see this
115 | output:
116 |
117 | :::text
118 | Usage: java -jar yuicompressor-x.y.z.jar [options] [input file]
119 |
120 | Global Options
121 | -h, --help Displays this information
122 | --type Specifies the type of the input file
123 | --charset Read the input file using
124 | --line-break Insert a line break after the specified column number
125 | -v, --verbose Display informational messages and warnings
126 | -o Place the output into . Defaults to stdout.
127 |
128 | JavaScript Options
129 | --nomunge Minify only, do not obfuscate
130 | --preserve-semi Preserve all semicolons
131 | --disable-optimizations Disable all micro optimizations
132 |
133 | If no input file is specified, it defaults to stdin. In this case, the 'type'
134 | option is required. Otherwise, the 'type' option is required only if the input
135 | file extension is neither 'js' nor 'css'.
136 |
137 |
138 | ## License
139 |
140 | `django-cssmin` is licensed under the following MIT/X11-style license:
141 |
142 | > Copyright (c) 2009 Zachary Voase
143 | >
144 | > Permission is hereby granted, free of charge, to any person
145 | > obtaining a copy of this software and associated documentation
146 | > files (the "Software"), to deal in the Software without
147 | > restriction, including without limitation the rights to use,
148 | > copy, modify, merge, publish, distribute, sublicense, and/or sell
149 | > copies of the Software, and to permit persons to whom the
150 | > Software is furnished to do so, subject to the following
151 | > conditions:
152 | >
153 | > The above copyright notice and this permission notice shall be
154 | > included in all copies or substantial portions of the Software.
155 | >
156 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
157 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
158 | > OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
159 | > NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
160 | > HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
161 | > WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
162 | > FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
163 | > OTHER DEALINGS IN THE SOFTWARE.
164 |
--------------------------------------------------------------------------------
/test/example/media/css/960/grid-24.css:
--------------------------------------------------------------------------------
1 | /*
2 | 960 Grid System ~ Core CSS.
3 | Learn more ~ http://960.gs/
4 |
5 | Licensed under GPL and MIT.
6 | */
7 |
8 | /* Container >> 24 Columns
9 | ----------------------------------------------------------------------------------------------------*/
10 | .container_24 {
11 | margin-left: auto;
12 | margin-right: auto;
13 | width: 960px;
14 | }
15 |
16 | /* Grid >> Global
17 | ----------------------------------------------------------------------------------------------------*/
18 |
19 | .grid_1,
20 | .grid_2,
21 | .grid_3,
22 | .grid_4,
23 | .grid_5,
24 | .grid_6,
25 | .grid_7,
26 | .grid_8,
27 | .grid_9,
28 | .grid_10,
29 | .grid_11,
30 | .grid_12,
31 | .grid_13,
32 | .grid_14,
33 | .grid_15,
34 | .grid_16,
35 | .grid_17,
36 | .grid_18,
37 | .grid_19,
38 | .grid_20,
39 | .grid_21,
40 | .grid_22,
41 | .grid_23,
42 | .grid_24 {
43 | display: inline;
44 | float: left;
45 | position: relative;
46 | margin-left: 5px;
47 | margin-right: 5px;
48 | }
49 |
50 | /* Grid >> Children (Alpha ~ First, Omega ~ Last)
51 | ----------------------------------------------------------------------------------------------------*/
52 |
53 | .alpha {
54 | margin-left: 0;
55 | }
56 |
57 | .omega {
58 | margin-right: 0;
59 | }
60 |
61 | /* Grid >> 24 Columns
62 | ----------------------------------------------------------------------------------------------------*/
63 |
64 | .container_24 .grid_1 {
65 | width: 30px;
66 | }
67 |
68 | .container_24 .grid_2 {
69 | width: 70px;
70 | }
71 |
72 | .container_24 .grid_3 {
73 | width: 110px;
74 | }
75 |
76 | .container_24 .grid_4 {
77 | width: 150px;
78 | }
79 |
80 | .container_24 .grid_5 {
81 | width: 190px;
82 | }
83 |
84 | .container_24 .grid_6 {
85 | width: 230px;
86 | }
87 |
88 | .container_24 .grid_7 {
89 | width: 270px;
90 | }
91 |
92 | .container_24 .grid_8 {
93 | width: 310px;
94 | }
95 |
96 | .container_24 .grid_9 {
97 | width: 350px;
98 | }
99 |
100 | .container_24 .grid_10 {
101 | width: 390px;
102 | }
103 |
104 | .container_24 .grid_11 {
105 | width: 430px;
106 | }
107 |
108 | .container_24 .grid_12 {
109 | width: 470px;
110 | }
111 |
112 | .container_24 .grid_13 {
113 | width: 510px;
114 | }
115 |
116 | .container_24 .grid_14 {
117 | width: 550px;
118 | }
119 |
120 | .container_24 .grid_15 {
121 | width: 590px;
122 | }
123 |
124 | .container_24 .grid_16 {
125 | width: 630px;
126 | }
127 |
128 | .container_24 .grid_17 {
129 | width: 670px;
130 | }
131 |
132 | .container_24 .grid_18 {
133 | width: 710px;
134 | }
135 |
136 | .container_24 .grid_19 {
137 | width: 750px;
138 | }
139 |
140 | .container_24 .grid_20 {
141 | width: 790px;
142 | }
143 |
144 | .container_24 .grid_21 {
145 | width: 830px;
146 | }
147 |
148 | .container_24 .grid_22 {
149 | width: 870px;
150 | }
151 |
152 | .container_24 .grid_23 {
153 | width: 910px;
154 | }
155 |
156 | .container_24 .grid_24 {
157 | width: 950px;
158 | }
159 |
160 | /* Prefix Extra Space >> 24 Columns
161 | ----------------------------------------------------------------------------------------------------*/
162 |
163 | .container_24 .prefix_1 {
164 | padding-left: 40px;
165 | }
166 |
167 | .container_24 .prefix_2 {
168 | padding-left: 80px;
169 | }
170 |
171 | .container_24 .prefix_3 {
172 | padding-left: 120px;
173 | }
174 |
175 | .container_24 .prefix_4 {
176 | padding-left: 160px;
177 | }
178 |
179 | .container_24 .prefix_5 {
180 | padding-left: 200px;
181 | }
182 |
183 | .container_24 .prefix_6 {
184 | padding-left: 240px;
185 | }
186 |
187 | .container_24 .prefix_7 {
188 | padding-left: 280px;
189 | }
190 |
191 | .container_24 .prefix_8 {
192 | padding-left: 320px;
193 | }
194 |
195 | .container_24 .prefix_9 {
196 | padding-left: 360px;
197 | }
198 |
199 | .container_24 .prefix_10 {
200 | padding-left: 400px;
201 | }
202 |
203 | .container_24 .prefix_11 {
204 | padding-left: 440px;
205 | }
206 |
207 | .container_24 .prefix_12 {
208 | padding-left: 480px;
209 | }
210 |
211 | .container_24 .prefix_13 {
212 | padding-left: 520px;
213 | }
214 |
215 | .container_24 .prefix_14 {
216 | padding-left: 560px;
217 | }
218 |
219 | .container_24 .prefix_15 {
220 | padding-left: 600px;
221 | }
222 |
223 | .container_24 .prefix_16 {
224 | padding-left: 640px;
225 | }
226 |
227 | .container_24 .prefix_17 {
228 | padding-left: 680px;
229 | }
230 |
231 | .container_24 .prefix_18 {
232 | padding-left: 720px;
233 | }
234 |
235 | .container_24 .prefix_19 {
236 | padding-left: 760px;
237 | }
238 |
239 | .container_24 .prefix_20 {
240 | padding-left: 800px;
241 | }
242 |
243 | .container_24 .prefix_21 {
244 | padding-left: 840px;
245 | }
246 |
247 | .container_24 .prefix_22 {
248 | padding-left: 880px;
249 | }
250 |
251 | .container_24 .prefix_23 {
252 | padding-left: 920px;
253 | }
254 |
255 | /* Suffix Extra Space >> 24 Columns
256 | ----------------------------------------------------------------------------------------------------*/
257 |
258 | .container_24 .suffix_1 {
259 | padding-right: 40px;
260 | }
261 |
262 | .container_24 .suffix_2 {
263 | padding-right: 80px;
264 | }
265 |
266 | .container_24 .suffix_3 {
267 | padding-right: 120px;
268 | }
269 |
270 | .container_24 .suffix_4 {
271 | padding-right: 160px;
272 | }
273 |
274 | .container_24 .suffix_5 {
275 | padding-right: 200px;
276 | }
277 |
278 | .container_24 .suffix_6 {
279 | padding-right: 240px;
280 | }
281 |
282 | .container_24 .suffix_7 {
283 | padding-right: 280px;
284 | }
285 |
286 | .container_24 .suffix_8 {
287 | padding-right: 320px;
288 | }
289 |
290 | .container_24 .suffix_9 {
291 | padding-right: 360px;
292 | }
293 |
294 | .container_24 .suffix_10 {
295 | padding-right: 400px;
296 | }
297 |
298 | .container_24 .suffix_11 {
299 | padding-right: 440px;
300 | }
301 |
302 | .container_24 .suffix_12 {
303 | padding-right: 480px;
304 | }
305 |
306 | .container_24 .suffix_13 {
307 | padding-right: 520px;
308 | }
309 |
310 | .container_24 .suffix_14 {
311 | padding-right: 560px;
312 | }
313 |
314 | .container_24 .suffix_15 {
315 | padding-right: 600px;
316 | }
317 |
318 | .container_24 .suffix_16 {
319 | padding-right: 640px;
320 | }
321 |
322 | .container_24 .suffix_17 {
323 | padding-right: 680px;
324 | }
325 |
326 | .container_24 .suffix_18 {
327 | padding-right: 720px;
328 | }
329 |
330 | .container_24 .suffix_19 {
331 | padding-right: 760px;
332 | }
333 |
334 | .container_24 .suffix_20 {
335 | padding-right: 800px;
336 | }
337 |
338 | .container_24 .suffix_21 {
339 | padding-right: 840px;
340 | }
341 |
342 | .container_24 .suffix_22 {
343 | padding-right: 880px;
344 | }
345 |
346 | .container_24 .suffix_23 {
347 | padding-right: 920px;
348 | }
349 |
350 | /* Push Space >> 24 Columns
351 | ----------------------------------------------------------------------------------------------------*/
352 |
353 | .container_24 .push_1 {
354 | left: 40px;
355 | }
356 |
357 | .container_24 .push_2 {
358 | left: 80px;
359 | }
360 |
361 | .container_24 .push_3 {
362 | left: 120px;
363 | }
364 |
365 | .container_24 .push_4 {
366 | left: 160px;
367 | }
368 |
369 | .container_24 .push_5 {
370 | left: 200px;
371 | }
372 |
373 | .container_24 .push_6 {
374 | left: 240px;
375 | }
376 |
377 | .container_24 .push_7 {
378 | left: 280px;
379 | }
380 |
381 | .container_24 .push_8 {
382 | left: 320px;
383 | }
384 |
385 | .container_24 .push_9 {
386 | left: 360px;
387 | }
388 |
389 | .container_24 .push_10 {
390 | left: 400px;
391 | }
392 |
393 | .container_24 .push_11 {
394 | left: 440px;
395 | }
396 |
397 | .container_24 .push_12 {
398 | left: 480px;
399 | }
400 |
401 | .container_24 .push_13 {
402 | left: 520px;
403 | }
404 |
405 | .container_24 .push_14 {
406 | left: 560px;
407 | }
408 |
409 | .container_24 .push_15 {
410 | left: 600px;
411 | }
412 |
413 | .container_24 .push_16 {
414 | left: 640px;
415 | }
416 |
417 | .container_24 .push_17 {
418 | left: 680px;
419 | }
420 |
421 | .container_24 .push_18 {
422 | left: 720px;
423 | }
424 |
425 | .container_24 .push_19 {
426 | left: 760px;
427 | }
428 |
429 | .container_24 .push_20 {
430 | left: 800px;
431 | }
432 |
433 | .container_24 .push_21 {
434 | left: 840px;
435 | }
436 |
437 | .container_24 .push_22 {
438 | left: 880px;
439 | }
440 |
441 | .container_24 .push_23 {
442 | left: 920px;
443 | }
444 |
445 | /* Pull Space >> 24 Columns
446 | ----------------------------------------------------------------------------------------------------*/
447 |
448 | .container_24 .pull_1 {
449 | left: -40px;
450 | }
451 |
452 | .container_24 .pull_2 {
453 | left: -80px;
454 | }
455 |
456 | .container_24 .pull_3 {
457 | left: -120px;
458 | }
459 |
460 | .container_24 .pull_4 {
461 | left: -160px;
462 | }
463 |
464 | .container_24 .pull_5 {
465 | left: -200px;
466 | }
467 |
468 | .container_24 .pull_6 {
469 | left: -240px;
470 | }
471 |
472 | .container_24 .pull_7 {
473 | left: -280px;
474 | }
475 |
476 | .container_24 .pull_8 {
477 | left: -320px;
478 | }
479 |
480 | .container_24 .pull_9 {
481 | left: -360px;
482 | }
483 |
484 | .container_24 .pull_10 {
485 | left: -400px;
486 | }
487 |
488 | .container_24 .pull_11 {
489 | left: -440px;
490 | }
491 |
492 | .container_24 .pull_12 {
493 | left: -480px;
494 | }
495 |
496 | .container_24 .pull_13 {
497 | left: -520px;
498 | }
499 |
500 | .container_24 .pull_14 {
501 | left: -560px;
502 | }
503 |
504 | .container_24 .pull_15 {
505 | left: -600px;
506 | }
507 |
508 | .container_24 .pull_16 {
509 | left: -640px;
510 | }
511 |
512 | .container_24 .pull_17 {
513 | left: -680px;
514 | }
515 |
516 | .container_24 .pull_18 {
517 | left: -720px;
518 | }
519 |
520 | .container_24 .pull_19 {
521 | left: -760px;
522 | }
523 |
524 | .container_24 .pull_20 {
525 | left: -800px;
526 | }
527 |
528 | .container_24 .pull_21 {
529 | left: -840px;
530 | }
531 |
532 | .container_24 .pull_22 {
533 | left: -880px;
534 | }
535 |
536 | .container_24 .pull_23 {
537 | left: -920px;
538 | }
539 |
540 | /* `Clear Floated Elements
541 | ----------------------------------------------------------------------------------------------------*/
542 |
543 | /* http://sonspring.com/journal/clearing-floats */
544 |
545 | .clear {
546 | clear: both;
547 | display: block;
548 | overflow: hidden;
549 | visibility: hidden;
550 | width: 0;
551 | height: 0;
552 | }
553 |
554 | /* http://perishablepress.com/press/2009/12/06/new-clearfix-hack */
555 |
556 | .clearfix:after {
557 | clear: both;
558 | content: ' ';
559 | display: block;
560 | font-size: 0;
561 | line-height: 0;
562 | visibility: hidden;
563 | width: 0;
564 | height: 0;
565 | }
566 |
567 | /*
568 | The following zoom:1 rule is specifically for IE6 + IE7.
569 | Move to separate stylesheet if invalid CSS is a problem.
570 | */
571 | * html .clearfix,
572 | *:first-child+html .clearfix {
573 | zoom: 1;
574 | }
--------------------------------------------------------------------------------
/test/example/media/css/960/grid.css:
--------------------------------------------------------------------------------
1 | /*
2 | 960 Grid System ~ Core CSS.
3 | Learn more ~ http://960.gs/
4 |
5 | Licensed under GPL and MIT.
6 | */
7 |
8 | /* `Containers
9 | ----------------------------------------------------------------------------------------------------*/
10 |
11 | .container_12,
12 | .container_16 {
13 | margin-left: auto;
14 | margin-right: auto;
15 | width: 960px;
16 | }
17 |
18 | /* `Grid >> Global
19 | ----------------------------------------------------------------------------------------------------*/
20 |
21 | .grid_1,
22 | .grid_2,
23 | .grid_3,
24 | .grid_4,
25 | .grid_5,
26 | .grid_6,
27 | .grid_7,
28 | .grid_8,
29 | .grid_9,
30 | .grid_10,
31 | .grid_11,
32 | .grid_12,
33 | .grid_13,
34 | .grid_14,
35 | .grid_15,
36 | .grid_16 {
37 | display: inline;
38 | float: left;
39 | position: relative;
40 | margin-left: 10px;
41 | margin-right: 10px;
42 | }
43 |
44 | .container_12 .grid_3,
45 | .container_16 .grid_4 {
46 | width: 220px;
47 | }
48 |
49 | .container_12 .grid_6,
50 | .container_16 .grid_8 {
51 | width: 460px;
52 | }
53 |
54 | .container_12 .grid_9,
55 | .container_16 .grid_12 {
56 | width: 700px;
57 | }
58 |
59 | .container_12 .grid_12,
60 | .container_16 .grid_16 {
61 | width: 940px;
62 | }
63 |
64 | /* `Grid >> Children (Alpha ~ First, Omega ~ Last)
65 | ----------------------------------------------------------------------------------------------------*/
66 |
67 | .alpha {
68 | margin-left: 0;
69 | }
70 |
71 | .omega {
72 | margin-right: 0;
73 | }
74 |
75 | /* `Grid >> 12 Columns
76 | ----------------------------------------------------------------------------------------------------*/
77 |
78 | .container_12 .grid_1 {
79 | width: 60px;
80 | }
81 |
82 | .container_12 .grid_2 {
83 | width: 140px;
84 | }
85 |
86 | .container_12 .grid_4 {
87 | width: 300px;
88 | }
89 |
90 | .container_12 .grid_5 {
91 | width: 380px;
92 | }
93 |
94 | .container_12 .grid_7 {
95 | width: 540px;
96 | }
97 |
98 | .container_12 .grid_8 {
99 | width: 620px;
100 | }
101 |
102 | .container_12 .grid_10 {
103 | width: 780px;
104 | }
105 |
106 | .container_12 .grid_11 {
107 | width: 860px;
108 | }
109 |
110 | /* `Grid >> 16 Columns
111 | ----------------------------------------------------------------------------------------------------*/
112 |
113 | .container_16 .grid_1 {
114 | width: 40px;
115 | }
116 |
117 | .container_16 .grid_2 {
118 | width: 100px;
119 | }
120 |
121 | .container_16 .grid_3 {
122 | width: 160px;
123 | }
124 |
125 | .container_16 .grid_5 {
126 | width: 280px;
127 | }
128 |
129 | .container_16 .grid_6 {
130 | width: 340px;
131 | }
132 |
133 | .container_16 .grid_7 {
134 | width: 400px;
135 | }
136 |
137 | .container_16 .grid_9 {
138 | width: 520px;
139 | }
140 |
141 | .container_16 .grid_10 {
142 | width: 580px;
143 | }
144 |
145 | .container_16 .grid_11 {
146 | width: 640px;
147 | }
148 |
149 | .container_16 .grid_13 {
150 | width: 760px;
151 | }
152 |
153 | .container_16 .grid_14 {
154 | width: 820px;
155 | }
156 |
157 | .container_16 .grid_15 {
158 | width: 880px;
159 | }
160 |
161 | /* `Prefix Extra Space >> Global
162 | ----------------------------------------------------------------------------------------------------*/
163 |
164 | .container_12 .prefix_3,
165 | .container_16 .prefix_4 {
166 | padding-left: 240px;
167 | }
168 |
169 | .container_12 .prefix_6,
170 | .container_16 .prefix_8 {
171 | padding-left: 480px;
172 | }
173 |
174 | .container_12 .prefix_9,
175 | .container_16 .prefix_12 {
176 | padding-left: 720px;
177 | }
178 |
179 | /* `Prefix Extra Space >> 12 Columns
180 | ----------------------------------------------------------------------------------------------------*/
181 |
182 | .container_12 .prefix_1 {
183 | padding-left: 80px;
184 | }
185 |
186 | .container_12 .prefix_2 {
187 | padding-left: 160px;
188 | }
189 |
190 | .container_12 .prefix_4 {
191 | padding-left: 320px;
192 | }
193 |
194 | .container_12 .prefix_5 {
195 | padding-left: 400px;
196 | }
197 |
198 | .container_12 .prefix_7 {
199 | padding-left: 560px;
200 | }
201 |
202 | .container_12 .prefix_8 {
203 | padding-left: 640px;
204 | }
205 |
206 | .container_12 .prefix_10 {
207 | padding-left: 800px;
208 | }
209 |
210 | .container_12 .prefix_11 {
211 | padding-left: 880px;
212 | }
213 |
214 | /* `Prefix Extra Space >> 16 Columns
215 | ----------------------------------------------------------------------------------------------------*/
216 |
217 | .container_16 .prefix_1 {
218 | padding-left: 60px;
219 | }
220 |
221 | .container_16 .prefix_2 {
222 | padding-left: 120px;
223 | }
224 |
225 | .container_16 .prefix_3 {
226 | padding-left: 180px;
227 | }
228 |
229 | .container_16 .prefix_5 {
230 | padding-left: 300px;
231 | }
232 |
233 | .container_16 .prefix_6 {
234 | padding-left: 360px;
235 | }
236 |
237 | .container_16 .prefix_7 {
238 | padding-left: 420px;
239 | }
240 |
241 | .container_16 .prefix_9 {
242 | padding-left: 540px;
243 | }
244 |
245 | .container_16 .prefix_10 {
246 | padding-left: 600px;
247 | }
248 |
249 | .container_16 .prefix_11 {
250 | padding-left: 660px;
251 | }
252 |
253 | .container_16 .prefix_13 {
254 | padding-left: 780px;
255 | }
256 |
257 | .container_16 .prefix_14 {
258 | padding-left: 840px;
259 | }
260 |
261 | .container_16 .prefix_15 {
262 | padding-left: 900px;
263 | }
264 |
265 | /* `Suffix Extra Space >> Global
266 | ----------------------------------------------------------------------------------------------------*/
267 |
268 | .container_12 .suffix_3,
269 | .container_16 .suffix_4 {
270 | padding-right: 240px;
271 | }
272 |
273 | .container_12 .suffix_6,
274 | .container_16 .suffix_8 {
275 | padding-right: 480px;
276 | }
277 |
278 | .container_12 .suffix_9,
279 | .container_16 .suffix_12 {
280 | padding-right: 720px;
281 | }
282 |
283 | /* `Suffix Extra Space >> 12 Columns
284 | ----------------------------------------------------------------------------------------------------*/
285 |
286 | .container_12 .suffix_1 {
287 | padding-right: 80px;
288 | }
289 |
290 | .container_12 .suffix_2 {
291 | padding-right: 160px;
292 | }
293 |
294 | .container_12 .suffix_4 {
295 | padding-right: 320px;
296 | }
297 |
298 | .container_12 .suffix_5 {
299 | padding-right: 400px;
300 | }
301 |
302 | .container_12 .suffix_7 {
303 | padding-right: 560px;
304 | }
305 |
306 | .container_12 .suffix_8 {
307 | padding-right: 640px;
308 | }
309 |
310 | .container_12 .suffix_10 {
311 | padding-right: 800px;
312 | }
313 |
314 | .container_12 .suffix_11 {
315 | padding-right: 880px;
316 | }
317 |
318 | /* `Suffix Extra Space >> 16 Columns
319 | ----------------------------------------------------------------------------------------------------*/
320 |
321 | .container_16 .suffix_1 {
322 | padding-right: 60px;
323 | }
324 |
325 | .container_16 .suffix_2 {
326 | padding-right: 120px;
327 | }
328 |
329 | .container_16 .suffix_3 {
330 | padding-right: 180px;
331 | }
332 |
333 | .container_16 .suffix_5 {
334 | padding-right: 300px;
335 | }
336 |
337 | .container_16 .suffix_6 {
338 | padding-right: 360px;
339 | }
340 |
341 | .container_16 .suffix_7 {
342 | padding-right: 420px;
343 | }
344 |
345 | .container_16 .suffix_9 {
346 | padding-right: 540px;
347 | }
348 |
349 | .container_16 .suffix_10 {
350 | padding-right: 600px;
351 | }
352 |
353 | .container_16 .suffix_11 {
354 | padding-right: 660px;
355 | }
356 |
357 | .container_16 .suffix_13 {
358 | padding-right: 780px;
359 | }
360 |
361 | .container_16 .suffix_14 {
362 | padding-right: 840px;
363 | }
364 |
365 | .container_16 .suffix_15 {
366 | padding-right: 900px;
367 | }
368 |
369 | /* `Push Space >> Global
370 | ----------------------------------------------------------------------------------------------------*/
371 |
372 | .container_12 .push_3,
373 | .container_16 .push_4 {
374 | left: 240px;
375 | }
376 |
377 | .container_12 .push_6,
378 | .container_16 .push_8 {
379 | left: 480px;
380 | }
381 |
382 | .container_12 .push_9,
383 | .container_16 .push_12 {
384 | left: 720px;
385 | }
386 |
387 | /* `Push Space >> 12 Columns
388 | ----------------------------------------------------------------------------------------------------*/
389 |
390 | .container_12 .push_1 {
391 | left: 80px;
392 | }
393 |
394 | .container_12 .push_2 {
395 | left: 160px;
396 | }
397 |
398 | .container_12 .push_4 {
399 | left: 320px;
400 | }
401 |
402 | .container_12 .push_5 {
403 | left: 400px;
404 | }
405 |
406 | .container_12 .push_7 {
407 | left: 560px;
408 | }
409 |
410 | .container_12 .push_8 {
411 | left: 640px;
412 | }
413 |
414 | .container_12 .push_10 {
415 | left: 800px;
416 | }
417 |
418 | .container_12 .push_11 {
419 | left: 880px;
420 | }
421 |
422 | /* `Push Space >> 16 Columns
423 | ----------------------------------------------------------------------------------------------------*/
424 |
425 | .container_16 .push_1 {
426 | left: 60px;
427 | }
428 |
429 | .container_16 .push_2 {
430 | left: 120px;
431 | }
432 |
433 | .container_16 .push_3 {
434 | left: 180px;
435 | }
436 |
437 | .container_16 .push_5 {
438 | left: 300px;
439 | }
440 |
441 | .container_16 .push_6 {
442 | left: 360px;
443 | }
444 |
445 | .container_16 .push_7 {
446 | left: 420px;
447 | }
448 |
449 | .container_16 .push_9 {
450 | left: 540px;
451 | }
452 |
453 | .container_16 .push_10 {
454 | left: 600px;
455 | }
456 |
457 | .container_16 .push_11 {
458 | left: 660px;
459 | }
460 |
461 | .container_16 .push_13 {
462 | left: 780px;
463 | }
464 |
465 | .container_16 .push_14 {
466 | left: 840px;
467 | }
468 |
469 | .container_16 .push_15 {
470 | left: 900px;
471 | }
472 |
473 | /* `Pull Space >> Global
474 | ----------------------------------------------------------------------------------------------------*/
475 |
476 | .container_12 .pull_3,
477 | .container_16 .pull_4 {
478 | left: -240px;
479 | }
480 |
481 | .container_12 .pull_6,
482 | .container_16 .pull_8 {
483 | left: -480px;
484 | }
485 |
486 | .container_12 .pull_9,
487 | .container_16 .pull_12 {
488 | left: -720px;
489 | }
490 |
491 | /* `Pull Space >> 12 Columns
492 | ----------------------------------------------------------------------------------------------------*/
493 |
494 | .container_12 .pull_1 {
495 | left: -80px;
496 | }
497 |
498 | .container_12 .pull_2 {
499 | left: -160px;
500 | }
501 |
502 | .container_12 .pull_4 {
503 | left: -320px;
504 | }
505 |
506 | .container_12 .pull_5 {
507 | left: -400px;
508 | }
509 |
510 | .container_12 .pull_7 {
511 | left: -560px;
512 | }
513 |
514 | .container_12 .pull_8 {
515 | left: -640px;
516 | }
517 |
518 | .container_12 .pull_10 {
519 | left: -800px;
520 | }
521 |
522 | .container_12 .pull_11 {
523 | left: -880px;
524 | }
525 |
526 | /* `Pull Space >> 16 Columns
527 | ----------------------------------------------------------------------------------------------------*/
528 |
529 | .container_16 .pull_1 {
530 | left: -60px;
531 | }
532 |
533 | .container_16 .pull_2 {
534 | left: -120px;
535 | }
536 |
537 | .container_16 .pull_3 {
538 | left: -180px;
539 | }
540 |
541 | .container_16 .pull_5 {
542 | left: -300px;
543 | }
544 |
545 | .container_16 .pull_6 {
546 | left: -360px;
547 | }
548 |
549 | .container_16 .pull_7 {
550 | left: -420px;
551 | }
552 |
553 | .container_16 .pull_9 {
554 | left: -540px;
555 | }
556 |
557 | .container_16 .pull_10 {
558 | left: -600px;
559 | }
560 |
561 | .container_16 .pull_11 {
562 | left: -660px;
563 | }
564 |
565 | .container_16 .pull_13 {
566 | left: -780px;
567 | }
568 |
569 | .container_16 .pull_14 {
570 | left: -840px;
571 | }
572 |
573 | .container_16 .pull_15 {
574 | left: -900px;
575 | }
576 |
577 | /* `Clear Floated Elements
578 | ----------------------------------------------------------------------------------------------------*/
579 |
580 | /* http://sonspring.com/journal/clearing-floats */
581 |
582 | .clear {
583 | clear: both;
584 | display: block;
585 | overflow: hidden;
586 | visibility: hidden;
587 | width: 0;
588 | height: 0;
589 | }
590 |
591 | /* http://perishablepress.com/press/2009/12/06/new-clearfix-hack */
592 |
593 | .clearfix:after {
594 | clear: both;
595 | content: ' ';
596 | display: block;
597 | font-size: 0;
598 | line-height: 0;
599 | visibility: hidden;
600 | width: 0;
601 | height: 0;
602 | }
603 |
604 | /*
605 | The following zoom:1 rule is specifically for IE6 + IE7.
606 | Move to separate stylesheet if invalid CSS is a problem.
607 | */
608 | * html .clearfix,
609 | *:first-child+html .clearfix {
610 | zoom: 1;
611 | }
--------------------------------------------------------------------------------
/distribute_setup.py:
--------------------------------------------------------------------------------
1 | #!python
2 | """Bootstrap distribute installation
3 |
4 | If you want to use setuptools in your package's setup.py, just include this
5 | file in the same directory with it, and add this to the top of your setup.py::
6 |
7 | from distribute_setup import use_setuptools
8 | use_setuptools()
9 |
10 | If you want to require a specific version of setuptools, set a download
11 | mirror, or use an alternate download directory, you can do so by supplying
12 | the appropriate options to ``use_setuptools()``.
13 |
14 | This file can also be run as a script to install or upgrade setuptools.
15 | """
16 | import os
17 | import sys
18 | import time
19 | import fnmatch
20 | import tempfile
21 | import tarfile
22 | from distutils import log
23 |
24 | try:
25 | from site import USER_SITE
26 | except ImportError:
27 | USER_SITE = None
28 |
29 | try:
30 | import subprocess
31 |
32 | def _python_cmd(*args):
33 | args = (sys.executable,) + args
34 | return subprocess.call(args) == 0
35 |
36 | except ImportError:
37 | # will be used for python 2.3
38 | def _python_cmd(*args):
39 | args = (sys.executable,) + args
40 | # quoting arguments if windows
41 | if sys.platform == 'win32':
42 | def quote(arg):
43 | if ' ' in arg:
44 | return '"%s"' % arg
45 | return arg
46 | args = [quote(arg) for arg in args]
47 | return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
48 |
49 | DEFAULT_VERSION = "0.6.10"
50 | DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
51 | SETUPTOOLS_FAKED_VERSION = "0.6c11"
52 |
53 | SETUPTOOLS_PKG_INFO = """\
54 | Metadata-Version: 1.0
55 | Name: setuptools
56 | Version: %s
57 | Summary: xxxx
58 | Home-page: xxx
59 | Author: xxx
60 | Author-email: xxx
61 | License: xxx
62 | Description: xxx
63 | """ % SETUPTOOLS_FAKED_VERSION
64 |
65 |
66 | def _install(tarball):
67 | # extracting the tarball
68 | tmpdir = tempfile.mkdtemp()
69 | log.warn('Extracting in %s', tmpdir)
70 | old_wd = os.getcwd()
71 | try:
72 | os.chdir(tmpdir)
73 | tar = tarfile.open(tarball)
74 | _extractall(tar)
75 | tar.close()
76 |
77 | # going in the directory
78 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
79 | os.chdir(subdir)
80 | log.warn('Now working in %s', subdir)
81 |
82 | # installing
83 | log.warn('Installing Distribute')
84 | if not _python_cmd('setup.py', 'install'):
85 | log.warn('Something went wrong during the installation.')
86 | log.warn('See the error message above.')
87 | finally:
88 | os.chdir(old_wd)
89 |
90 |
91 | def _build_egg(egg, tarball, to_dir):
92 | # extracting the tarball
93 | tmpdir = tempfile.mkdtemp()
94 | log.warn('Extracting in %s', tmpdir)
95 | old_wd = os.getcwd()
96 | try:
97 | os.chdir(tmpdir)
98 | tar = tarfile.open(tarball)
99 | _extractall(tar)
100 | tar.close()
101 |
102 | # going in the directory
103 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
104 | os.chdir(subdir)
105 | log.warn('Now working in %s', subdir)
106 |
107 | # building an egg
108 | log.warn('Building a Distribute egg in %s', to_dir)
109 | _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
110 |
111 | finally:
112 | os.chdir(old_wd)
113 | # returning the result
114 | log.warn(egg)
115 | if not os.path.exists(egg):
116 | raise IOError('Could not build the egg.')
117 |
118 |
119 | def _do_download(version, download_base, to_dir, download_delay):
120 | egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
121 | % (version, sys.version_info[0], sys.version_info[1]))
122 | if not os.path.exists(egg):
123 | tarball = download_setuptools(version, download_base,
124 | to_dir, download_delay)
125 | _build_egg(egg, tarball, to_dir)
126 | sys.path.insert(0, egg)
127 | import setuptools
128 | setuptools.bootstrap_install_from = egg
129 |
130 |
131 | def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
132 | to_dir=os.curdir, download_delay=15, no_fake=True):
133 | # making sure we use the absolute path
134 | to_dir = os.path.abspath(to_dir)
135 | was_imported = 'pkg_resources' in sys.modules or \
136 | 'setuptools' in sys.modules
137 | try:
138 | try:
139 | import pkg_resources
140 | if not hasattr(pkg_resources, '_distribute'):
141 | if not no_fake:
142 | _fake_setuptools()
143 | raise ImportError
144 | except ImportError:
145 | return _do_download(version, download_base, to_dir, download_delay)
146 | try:
147 | pkg_resources.require("distribute>="+version)
148 | return
149 | except pkg_resources.VersionConflict:
150 | e = sys.exc_info()[1]
151 | if was_imported:
152 | sys.stderr.write(
153 | "The required version of distribute (>=%s) is not available,\n"
154 | "and can't be installed while this script is running. Please\n"
155 | "install a more recent version first, using\n"
156 | "'easy_install -U distribute'."
157 | "\n\n(Currently using %r)\n" % (version, e.args[0]))
158 | sys.exit(2)
159 | else:
160 | del pkg_resources, sys.modules['pkg_resources'] # reload ok
161 | return _do_download(version, download_base, to_dir,
162 | download_delay)
163 | except pkg_resources.DistributionNotFound:
164 | return _do_download(version, download_base, to_dir,
165 | download_delay)
166 | finally:
167 | if not no_fake:
168 | _create_fake_setuptools_pkg_info(to_dir)
169 |
170 | def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
171 | to_dir=os.curdir, delay=15):
172 | """Download distribute from a specified location and return its filename
173 |
174 | `version` should be a valid distribute version number that is available
175 | as an egg for download under the `download_base` URL (which should end
176 | with a '/'). `to_dir` is the directory where the egg will be downloaded.
177 | `delay` is the number of seconds to pause before an actual download
178 | attempt.
179 | """
180 | # making sure we use the absolute path
181 | to_dir = os.path.abspath(to_dir)
182 | try:
183 | from urllib.request import urlopen
184 | except ImportError:
185 | from urllib2 import urlopen
186 | tgz_name = "distribute-%s.tar.gz" % version
187 | url = download_base + tgz_name
188 | saveto = os.path.join(to_dir, tgz_name)
189 | src = dst = None
190 | if not os.path.exists(saveto): # Avoid repeated downloads
191 | try:
192 | log.warn("Downloading %s", url)
193 | src = urlopen(url)
194 | # Read/write all in one block, so we don't create a corrupt file
195 | # if the download is interrupted.
196 | data = src.read()
197 | dst = open(saveto, "wb")
198 | dst.write(data)
199 | finally:
200 | if src:
201 | src.close()
202 | if dst:
203 | dst.close()
204 | return os.path.realpath(saveto)
205 |
206 |
207 | def _patch_file(path, content):
208 | """Will backup the file then patch it"""
209 | existing_content = open(path).read()
210 | if existing_content == content:
211 | # already patched
212 | log.warn('Already patched.')
213 | return False
214 | log.warn('Patching...')
215 | _rename_path(path)
216 | f = open(path, 'w')
217 | try:
218 | f.write(content)
219 | finally:
220 | f.close()
221 | return True
222 |
223 |
224 | def _same_content(path, content):
225 | return open(path).read() == content
226 |
227 | def _no_sandbox(function):
228 | def __no_sandbox(*args, **kw):
229 | try:
230 | from setuptools.sandbox import DirectorySandbox
231 | def violation(*args):
232 | pass
233 | DirectorySandbox._old = DirectorySandbox._violation
234 | DirectorySandbox._violation = violation
235 | patched = True
236 | except ImportError:
237 | patched = False
238 |
239 | try:
240 | return function(*args, **kw)
241 | finally:
242 | if patched:
243 | DirectorySandbox._violation = DirectorySandbox._old
244 | del DirectorySandbox._old
245 |
246 | return __no_sandbox
247 |
248 | @_no_sandbox
249 | def _rename_path(path):
250 | new_name = path + '.OLD.%s' % time.time()
251 | log.warn('Renaming %s into %s', path, new_name)
252 | os.rename(path, new_name)
253 | return new_name
254 |
255 | def _remove_flat_installation(placeholder):
256 | if not os.path.isdir(placeholder):
257 | log.warn('Unkown installation at %s', placeholder)
258 | return False
259 | found = False
260 | for file in os.listdir(placeholder):
261 | if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
262 | found = True
263 | break
264 | if not found:
265 | log.warn('Could not locate setuptools*.egg-info')
266 | return
267 |
268 | log.warn('Removing elements out of the way...')
269 | pkg_info = os.path.join(placeholder, file)
270 | if os.path.isdir(pkg_info):
271 | patched = _patch_egg_dir(pkg_info)
272 | else:
273 | patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
274 |
275 | if not patched:
276 | log.warn('%s already patched.', pkg_info)
277 | return False
278 | # now let's move the files out of the way
279 | for element in ('setuptools', 'pkg_resources.py', 'site.py'):
280 | element = os.path.join(placeholder, element)
281 | if os.path.exists(element):
282 | _rename_path(element)
283 | else:
284 | log.warn('Could not find the %s element of the '
285 | 'Setuptools distribution', element)
286 | return True
287 |
288 |
289 | def _after_install(dist):
290 | log.warn('After install bootstrap.')
291 | placeholder = dist.get_command_obj('install').install_purelib
292 | _create_fake_setuptools_pkg_info(placeholder)
293 |
294 | @_no_sandbox
295 | def _create_fake_setuptools_pkg_info(placeholder):
296 | if not placeholder or not os.path.exists(placeholder):
297 | log.warn('Could not find the install location')
298 | return
299 | pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
300 | setuptools_file = 'setuptools-%s-py%s.egg-info' % \
301 | (SETUPTOOLS_FAKED_VERSION, pyver)
302 | pkg_info = os.path.join(placeholder, setuptools_file)
303 | if os.path.exists(pkg_info):
304 | log.warn('%s already exists', pkg_info)
305 | return
306 |
307 | log.warn('Creating %s', pkg_info)
308 | f = open(pkg_info, 'w')
309 | try:
310 | f.write(SETUPTOOLS_PKG_INFO)
311 | finally:
312 | f.close()
313 |
314 | pth_file = os.path.join(placeholder, 'setuptools.pth')
315 | log.warn('Creating %s', pth_file)
316 | f = open(pth_file, 'w')
317 | try:
318 | f.write(os.path.join(os.curdir, setuptools_file))
319 | finally:
320 | f.close()
321 |
322 | def _patch_egg_dir(path):
323 | # let's check if it's already patched
324 | pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
325 | if os.path.exists(pkg_info):
326 | if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
327 | log.warn('%s already patched.', pkg_info)
328 | return False
329 | _rename_path(path)
330 | os.mkdir(path)
331 | os.mkdir(os.path.join(path, 'EGG-INFO'))
332 | pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
333 | f = open(pkg_info, 'w')
334 | try:
335 | f.write(SETUPTOOLS_PKG_INFO)
336 | finally:
337 | f.close()
338 | return True
339 |
340 |
341 | def _before_install():
342 | log.warn('Before install bootstrap.')
343 | _fake_setuptools()
344 |
345 |
346 | def _under_prefix(location):
347 | if 'install' not in sys.argv:
348 | return True
349 | args = sys.argv[sys.argv.index('install')+1:]
350 | for index, arg in enumerate(args):
351 | for option in ('--root', '--prefix'):
352 | if arg.startswith('%s=' % option):
353 | top_dir = arg.split('root=')[-1]
354 | return location.startswith(top_dir)
355 | elif arg == option:
356 | if len(args) > index:
357 | top_dir = args[index+1]
358 | return location.startswith(top_dir)
359 | elif option == '--user' and USER_SITE is not None:
360 | return location.startswith(USER_SITE)
361 | return True
362 |
363 |
364 | def _fake_setuptools():
365 | log.warn('Scanning installed packages')
366 | try:
367 | import pkg_resources
368 | except ImportError:
369 | # we're cool
370 | log.warn('Setuptools or Distribute does not seem to be installed.')
371 | return
372 | ws = pkg_resources.working_set
373 | try:
374 | setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
375 | replacement=False))
376 | except TypeError:
377 | # old distribute API
378 | setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
379 |
380 | if setuptools_dist is None:
381 | log.warn('No setuptools distribution found')
382 | return
383 | # detecting if it was already faked
384 | setuptools_location = setuptools_dist.location
385 | log.warn('Setuptools installation detected at %s', setuptools_location)
386 |
387 | # if --root or --preix was provided, and if
388 | # setuptools is not located in them, we don't patch it
389 | if not _under_prefix(setuptools_location):
390 | log.warn('Not patching, --root or --prefix is installing Distribute'
391 | ' in another location')
392 | return
393 |
394 | # let's see if its an egg
395 | if not setuptools_location.endswith('.egg'):
396 | log.warn('Non-egg installation')
397 | res = _remove_flat_installation(setuptools_location)
398 | if not res:
399 | return
400 | else:
401 | log.warn('Egg installation')
402 | pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
403 | if (os.path.exists(pkg_info) and
404 | _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
405 | log.warn('Already patched.')
406 | return
407 | log.warn('Patching...')
408 | # let's create a fake egg replacing setuptools one
409 | res = _patch_egg_dir(setuptools_location)
410 | if not res:
411 | return
412 | log.warn('Patched done.')
413 | _relaunch()
414 |
415 |
416 | def _relaunch():
417 | log.warn('Relaunching...')
418 | # we have to relaunch the process
419 | args = [sys.executable] + sys.argv
420 | sys.exit(subprocess.call(args))
421 |
422 |
423 | def _extractall(self, path=".", members=None):
424 | """Extract all members from the archive to the current working
425 | directory and set owner, modification time and permissions on
426 | directories afterwards. `path' specifies a different directory
427 | to extract to. `members' is optional and must be a subset of the
428 | list returned by getmembers().
429 | """
430 | import copy
431 | import operator
432 | from tarfile import ExtractError
433 | directories = []
434 |
435 | if members is None:
436 | members = self
437 |
438 | for tarinfo in members:
439 | if tarinfo.isdir():
440 | # Extract directories with a safe mode.
441 | directories.append(tarinfo)
442 | tarinfo = copy.copy(tarinfo)
443 | tarinfo.mode = 448 # decimal for oct 0700
444 | self.extract(tarinfo, path)
445 |
446 | # Reverse sort directories.
447 | if sys.version_info < (2, 4):
448 | def sorter(dir1, dir2):
449 | return cmp(dir1.name, dir2.name)
450 | directories.sort(sorter)
451 | directories.reverse()
452 | else:
453 | directories.sort(key=operator.attrgetter('name'), reverse=True)
454 |
455 | # Set correct owner, mtime and filemode on directories.
456 | for tarinfo in directories:
457 | dirpath = os.path.join(path, tarinfo.name)
458 | try:
459 | self.chown(tarinfo, dirpath)
460 | self.utime(tarinfo, dirpath)
461 | self.chmod(tarinfo, dirpath)
462 | except ExtractError:
463 | e = sys.exc_info()[1]
464 | if self.errorlevel > 1:
465 | raise
466 | else:
467 | self._dbg(1, "tarfile: %s" % e)
468 |
469 |
470 | def main(argv, version=DEFAULT_VERSION):
471 | """Install or upgrade setuptools and EasyInstall"""
472 | tarball = download_setuptools()
473 | _install(tarball)
474 |
475 |
476 | if __name__ == '__main__':
477 | main(sys.argv[1:])
478 |
--------------------------------------------------------------------------------