Version 1.8
parent
4d96f79d78
commit
299d105b22
|
@ -1,2 +1,2 @@
|
||||||
# PortalAuth
|
# PortalAuth
|
||||||
Captive portal cloner and payload distributor for the WiFi Pineapple NANO and TETRA
|
Captive portal cloner and payload distributor for the WiFi Pineapple NANO and TETRA
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
July 26, 2018
|
||||||
|
<br /><br />
|
||||||
|
- Fixed tinycss lib reference<br />
|
File diff suppressed because it is too large
Load Diff
|
@ -1,310 +1,310 @@
|
||||||
# Copyright 2001-2005 by Vinay Sajip. All Rights Reserved.
|
# Copyright 2001-2005 by Vinay Sajip. All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Permission to use, copy, modify, and distribute this software and its
|
# Permission to use, copy, modify, and distribute this software and its
|
||||||
# documentation for any purpose and without fee is hereby granted,
|
# documentation for any purpose and without fee is hereby granted,
|
||||||
# provided that the above copyright notice appear in all copies and that
|
# provided that the above copyright notice appear in all copies and that
|
||||||
# both that copyright notice and this permission notice appear in
|
# both that copyright notice and this permission notice appear in
|
||||||
# supporting documentation, and that the name of Vinay Sajip
|
# supporting documentation, and that the name of Vinay Sajip
|
||||||
# not be used in advertising or publicity pertaining to distribution
|
# not be used in advertising or publicity pertaining to distribution
|
||||||
# of the software without specific, written prior permission.
|
# of the software without specific, written prior permission.
|
||||||
# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
||||||
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
||||||
# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
||||||
# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||||
# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||||
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Configuration functions for the logging package for Python. The core package
|
Configuration functions for the logging package for Python. The core package
|
||||||
is based on PEP 282 and comments thereto in comp.lang.python, and influenced
|
is based on PEP 282 and comments thereto in comp.lang.python, and influenced
|
||||||
by Apache's log4j system.
|
by Apache's log4j system.
|
||||||
|
|
||||||
Should work under Python versions >= 1.5.2, except that source line
|
Should work under Python versions >= 1.5.2, except that source line
|
||||||
information is not available unless 'sys._getframe()' is.
|
information is not available unless 'sys._getframe()' is.
|
||||||
|
|
||||||
Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved.
|
Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved.
|
||||||
|
|
||||||
To use, simply 'import logging' and log away!
|
To use, simply 'import logging' and log away!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys, logging, logging.handlers, string, socket, struct, os
|
import sys, logging, logging.handlers, string, socket, struct, os
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import thread
|
import thread
|
||||||
import threading
|
import threading
|
||||||
except ImportError:
|
except ImportError:
|
||||||
thread = None
|
thread = None
|
||||||
|
|
||||||
from SocketServer import ThreadingTCPServer, StreamRequestHandler
|
from SocketServer import ThreadingTCPServer, StreamRequestHandler
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_LOGGING_CONFIG_PORT = 9030
|
DEFAULT_LOGGING_CONFIG_PORT = 9030
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
RESET_ERROR = 10054 #WSAECONNRESET
|
RESET_ERROR = 10054 #WSAECONNRESET
|
||||||
else:
|
else:
|
||||||
RESET_ERROR = 104 #ECONNRESET
|
RESET_ERROR = 104 #ECONNRESET
|
||||||
|
|
||||||
#
|
#
|
||||||
# The following code implements a socket listener for on-the-fly
|
# The following code implements a socket listener for on-the-fly
|
||||||
# reconfiguration of logging.
|
# reconfiguration of logging.
|
||||||
#
|
#
|
||||||
# _listener holds the server object doing the listening
|
# _listener holds the server object doing the listening
|
||||||
_listener = None
|
_listener = None
|
||||||
|
|
||||||
def fileConfig(fname, defaults=None):
|
def fileConfig(fname, defaults=None):
|
||||||
"""
|
"""
|
||||||
Read the logging configuration from a ConfigParser-format file.
|
Read the logging configuration from a ConfigParser-format file.
|
||||||
|
|
||||||
This can be called several times from an application, allowing an end user
|
This can be called several times from an application, allowing an end user
|
||||||
the ability to select from various pre-canned configurations (if the
|
the ability to select from various pre-canned configurations (if the
|
||||||
developer provides a mechanism to present the choices and load the chosen
|
developer provides a mechanism to present the choices and load the chosen
|
||||||
configuration).
|
configuration).
|
||||||
In versions of ConfigParser which have the readfp method [typically
|
In versions of ConfigParser which have the readfp method [typically
|
||||||
shipped in 2.x versions of Python], you can pass in a file-like object
|
shipped in 2.x versions of Python], you can pass in a file-like object
|
||||||
rather than a filename, in which case the file-like object will be read
|
rather than a filename, in which case the file-like object will be read
|
||||||
using readfp.
|
using readfp.
|
||||||
"""
|
"""
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
|
||||||
cp = ConfigParser.ConfigParser(defaults)
|
cp = ConfigParser.ConfigParser(defaults)
|
||||||
if hasattr(cp, 'readfp') and hasattr(fname, 'readline'):
|
if hasattr(cp, 'readfp') and hasattr(fname, 'readline'):
|
||||||
cp.readfp(fname)
|
cp.readfp(fname)
|
||||||
else:
|
else:
|
||||||
cp.read(fname)
|
cp.read(fname)
|
||||||
#first, do the formatters...
|
#first, do the formatters...
|
||||||
flist = cp.get("formatters", "keys")
|
flist = cp.get("formatters", "keys")
|
||||||
if len(flist):
|
if len(flist):
|
||||||
flist = string.split(flist, ",")
|
flist = string.split(flist, ",")
|
||||||
formatters = {}
|
formatters = {}
|
||||||
for form in flist:
|
for form in flist:
|
||||||
sectname = "formatter_%s" % form
|
sectname = "formatter_%s" % form
|
||||||
opts = cp.options(sectname)
|
opts = cp.options(sectname)
|
||||||
if "format" in opts:
|
if "format" in opts:
|
||||||
fs = cp.get(sectname, "format", 1)
|
fs = cp.get(sectname, "format", 1)
|
||||||
else:
|
else:
|
||||||
fs = None
|
fs = None
|
||||||
if "datefmt" in opts:
|
if "datefmt" in opts:
|
||||||
dfs = cp.get(sectname, "datefmt", 1)
|
dfs = cp.get(sectname, "datefmt", 1)
|
||||||
else:
|
else:
|
||||||
dfs = None
|
dfs = None
|
||||||
f = logging.Formatter(fs, dfs)
|
f = logging.Formatter(fs, dfs)
|
||||||
formatters[form] = f
|
formatters[form] = f
|
||||||
#next, do the handlers...
|
#next, do the handlers...
|
||||||
#critical section...
|
#critical section...
|
||||||
logging._acquireLock()
|
logging._acquireLock()
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
#first, lose the existing handlers...
|
#first, lose the existing handlers...
|
||||||
logging._handlers.clear()
|
logging._handlers.clear()
|
||||||
#now set up the new ones...
|
#now set up the new ones...
|
||||||
hlist = cp.get("handlers", "keys")
|
hlist = cp.get("handlers", "keys")
|
||||||
if len(hlist):
|
if len(hlist):
|
||||||
hlist = string.split(hlist, ",")
|
hlist = string.split(hlist, ",")
|
||||||
handlers = {}
|
handlers = {}
|
||||||
fixups = [] #for inter-handler references
|
fixups = [] #for inter-handler references
|
||||||
for hand in hlist:
|
for hand in hlist:
|
||||||
try:
|
try:
|
||||||
sectname = "handler_%s" % hand
|
sectname = "handler_%s" % hand
|
||||||
klass = cp.get(sectname, "class")
|
klass = cp.get(sectname, "class")
|
||||||
opts = cp.options(sectname)
|
opts = cp.options(sectname)
|
||||||
if "formatter" in opts:
|
if "formatter" in opts:
|
||||||
fmt = cp.get(sectname, "formatter")
|
fmt = cp.get(sectname, "formatter")
|
||||||
else:
|
else:
|
||||||
fmt = ""
|
fmt = ""
|
||||||
klass = eval(klass, vars(logging))
|
klass = eval(klass, vars(logging))
|
||||||
args = cp.get(sectname, "args")
|
args = cp.get(sectname, "args")
|
||||||
args = eval(args, vars(logging))
|
args = eval(args, vars(logging))
|
||||||
h = apply(klass, args)
|
h = apply(klass, args)
|
||||||
if "level" in opts:
|
if "level" in opts:
|
||||||
level = cp.get(sectname, "level")
|
level = cp.get(sectname, "level")
|
||||||
h.setLevel(logging._levelNames[level])
|
h.setLevel(logging._levelNames[level])
|
||||||
if len(fmt):
|
if len(fmt):
|
||||||
h.setFormatter(formatters[fmt])
|
h.setFormatter(formatters[fmt])
|
||||||
#temporary hack for FileHandler and MemoryHandler.
|
#temporary hack for FileHandler and MemoryHandler.
|
||||||
if klass == logging.handlers.MemoryHandler:
|
if klass == logging.handlers.MemoryHandler:
|
||||||
if "target" in opts:
|
if "target" in opts:
|
||||||
target = cp.get(sectname,"target")
|
target = cp.get(sectname,"target")
|
||||||
else:
|
else:
|
||||||
target = ""
|
target = ""
|
||||||
if len(target): #the target handler may not be loaded yet, so keep for later...
|
if len(target): #the target handler may not be loaded yet, so keep for later...
|
||||||
fixups.append((h, target))
|
fixups.append((h, target))
|
||||||
handlers[hand] = h
|
handlers[hand] = h
|
||||||
except: #if an error occurs when instantiating a handler, too bad
|
except: #if an error occurs when instantiating a handler, too bad
|
||||||
pass #this could happen e.g. because of lack of privileges
|
pass #this could happen e.g. because of lack of privileges
|
||||||
#now all handlers are loaded, fixup inter-handler references...
|
#now all handlers are loaded, fixup inter-handler references...
|
||||||
for fixup in fixups:
|
for fixup in fixups:
|
||||||
h = fixup[0]
|
h = fixup[0]
|
||||||
t = fixup[1]
|
t = fixup[1]
|
||||||
h.setTarget(handlers[t])
|
h.setTarget(handlers[t])
|
||||||
#at last, the loggers...first the root...
|
#at last, the loggers...first the root...
|
||||||
llist = cp.get("loggers", "keys")
|
llist = cp.get("loggers", "keys")
|
||||||
llist = string.split(llist, ",")
|
llist = string.split(llist, ",")
|
||||||
llist.remove("root")
|
llist.remove("root")
|
||||||
sectname = "logger_root"
|
sectname = "logger_root"
|
||||||
root = logging.root
|
root = logging.root
|
||||||
log = root
|
log = root
|
||||||
opts = cp.options(sectname)
|
opts = cp.options(sectname)
|
||||||
if "level" in opts:
|
if "level" in opts:
|
||||||
level = cp.get(sectname, "level")
|
level = cp.get(sectname, "level")
|
||||||
log.setLevel(logging._levelNames[level])
|
log.setLevel(logging._levelNames[level])
|
||||||
for h in root.handlers[:]:
|
for h in root.handlers[:]:
|
||||||
root.removeHandler(h)
|
root.removeHandler(h)
|
||||||
hlist = cp.get(sectname, "handlers")
|
hlist = cp.get(sectname, "handlers")
|
||||||
if len(hlist):
|
if len(hlist):
|
||||||
hlist = string.split(hlist, ",")
|
hlist = string.split(hlist, ",")
|
||||||
for hand in hlist:
|
for hand in hlist:
|
||||||
log.addHandler(handlers[hand])
|
log.addHandler(handlers[hand])
|
||||||
#and now the others...
|
#and now the others...
|
||||||
#we don't want to lose the existing loggers,
|
#we don't want to lose the existing loggers,
|
||||||
#since other threads may have pointers to them.
|
#since other threads may have pointers to them.
|
||||||
#existing is set to contain all existing loggers,
|
#existing is set to contain all existing loggers,
|
||||||
#and as we go through the new configuration we
|
#and as we go through the new configuration we
|
||||||
#remove any which are configured. At the end,
|
#remove any which are configured. At the end,
|
||||||
#what's left in existing is the set of loggers
|
#what's left in existing is the set of loggers
|
||||||
#which were in the previous configuration but
|
#which were in the previous configuration but
|
||||||
#which are not in the new configuration.
|
#which are not in the new configuration.
|
||||||
existing = root.manager.loggerDict.keys()
|
existing = root.manager.loggerDict.keys()
|
||||||
#now set up the new ones...
|
#now set up the new ones...
|
||||||
for log in llist:
|
for log in llist:
|
||||||
sectname = "logger_%s" % log
|
sectname = "logger_%s" % log
|
||||||
qn = cp.get(sectname, "qualname")
|
qn = cp.get(sectname, "qualname")
|
||||||
opts = cp.options(sectname)
|
opts = cp.options(sectname)
|
||||||
if "propagate" in opts:
|
if "propagate" in opts:
|
||||||
propagate = cp.getint(sectname, "propagate")
|
propagate = cp.getint(sectname, "propagate")
|
||||||
else:
|
else:
|
||||||
propagate = 1
|
propagate = 1
|
||||||
logger = logging.getLogger(qn)
|
logger = logging.getLogger(qn)
|
||||||
if qn in existing:
|
if qn in existing:
|
||||||
existing.remove(qn)
|
existing.remove(qn)
|
||||||
if "level" in opts:
|
if "level" in opts:
|
||||||
level = cp.get(sectname, "level")
|
level = cp.get(sectname, "level")
|
||||||
logger.setLevel(logging._levelNames[level])
|
logger.setLevel(logging._levelNames[level])
|
||||||
for h in logger.handlers[:]:
|
for h in logger.handlers[:]:
|
||||||
logger.removeHandler(h)
|
logger.removeHandler(h)
|
||||||
logger.propagate = propagate
|
logger.propagate = propagate
|
||||||
logger.disabled = 0
|
logger.disabled = 0
|
||||||
hlist = cp.get(sectname, "handlers")
|
hlist = cp.get(sectname, "handlers")
|
||||||
if len(hlist):
|
if len(hlist):
|
||||||
hlist = string.split(hlist, ",")
|
hlist = string.split(hlist, ",")
|
||||||
for hand in hlist:
|
for hand in hlist:
|
||||||
logger.addHandler(handlers[hand])
|
logger.addHandler(handlers[hand])
|
||||||
#Disable any old loggers. There's no point deleting
|
#Disable any old loggers. There's no point deleting
|
||||||
#them as other threads may continue to hold references
|
#them as other threads may continue to hold references
|
||||||
#and by disabling them, you stop them doing any logging.
|
#and by disabling them, you stop them doing any logging.
|
||||||
for log in existing:
|
for log in existing:
|
||||||
root.manager.loggerDict[log].disabled = 1
|
root.manager.loggerDict[log].disabled = 1
|
||||||
except:
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
ei = sys.exc_info()
|
ei = sys.exc_info()
|
||||||
traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
|
traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
|
||||||
del ei
|
del ei
|
||||||
finally:
|
finally:
|
||||||
logging._releaseLock()
|
logging._releaseLock()
|
||||||
|
|
||||||
def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
|
def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
|
||||||
"""
|
"""
|
||||||
Start up a socket server on the specified port, and listen for new
|
Start up a socket server on the specified port, and listen for new
|
||||||
configurations.
|
configurations.
|
||||||
|
|
||||||
These will be sent as a file suitable for processing by fileConfig().
|
These will be sent as a file suitable for processing by fileConfig().
|
||||||
Returns a Thread object on which you can call start() to start the server,
|
Returns a Thread object on which you can call start() to start the server,
|
||||||
and which you can join() when appropriate. To stop the server, call
|
and which you can join() when appropriate. To stop the server, call
|
||||||
stopListening().
|
stopListening().
|
||||||
"""
|
"""
|
||||||
if not thread:
|
if not thread:
|
||||||
raise NotImplementedError, "listen() needs threading to work"
|
raise NotImplementedError, "listen() needs threading to work"
|
||||||
|
|
||||||
class ConfigStreamHandler(StreamRequestHandler):
|
class ConfigStreamHandler(StreamRequestHandler):
|
||||||
"""
|
"""
|
||||||
Handler for a logging configuration request.
|
Handler for a logging configuration request.
|
||||||
|
|
||||||
It expects a completely new logging configuration and uses fileConfig
|
It expects a completely new logging configuration and uses fileConfig
|
||||||
to install it.
|
to install it.
|
||||||
"""
|
"""
|
||||||
def handle(self):
|
def handle(self):
|
||||||
"""
|
"""
|
||||||
Handle a request.
|
Handle a request.
|
||||||
|
|
||||||
Each request is expected to be a 4-byte length,
|
Each request is expected to be a 4-byte length,
|
||||||
followed by the config file. Uses fileConfig() to do the
|
followed by the config file. Uses fileConfig() to do the
|
||||||
grunt work.
|
grunt work.
|
||||||
"""
|
"""
|
||||||
import tempfile
|
import tempfile
|
||||||
try:
|
try:
|
||||||
conn = self.connection
|
conn = self.connection
|
||||||
chunk = conn.recv(4)
|
chunk = conn.recv(4)
|
||||||
if len(chunk) == 4:
|
if len(chunk) == 4:
|
||||||
slen = struct.unpack(">L", chunk)[0]
|
slen = struct.unpack(">L", chunk)[0]
|
||||||
chunk = self.connection.recv(slen)
|
chunk = self.connection.recv(slen)
|
||||||
while len(chunk) < slen:
|
while len(chunk) < slen:
|
||||||
chunk = chunk + conn.recv(slen - len(chunk))
|
chunk = chunk + conn.recv(slen - len(chunk))
|
||||||
#Apply new configuration. We'd like to be able to
|
#Apply new configuration. We'd like to be able to
|
||||||
#create a StringIO and pass that in, but unfortunately
|
#create a StringIO and pass that in, but unfortunately
|
||||||
#1.5.2 ConfigParser does not support reading file
|
#1.5.2 ConfigParser does not support reading file
|
||||||
#objects, only actual files. So we create a temporary
|
#objects, only actual files. So we create a temporary
|
||||||
#file and remove it later.
|
#file and remove it later.
|
||||||
file = tempfile.mktemp(".ini")
|
file = tempfile.mktemp(".ini")
|
||||||
f = open(file, "w")
|
f = open(file, "w")
|
||||||
f.write(chunk)
|
f.write(chunk)
|
||||||
f.close()
|
f.close()
|
||||||
fileConfig(file)
|
fileConfig(file)
|
||||||
os.remove(file)
|
os.remove(file)
|
||||||
except socket.error, e:
|
except socket.error, e:
|
||||||
if type(e.args) != types.TupleType:
|
if type(e.args) != types.TupleType:
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
errcode = e.args[0]
|
errcode = e.args[0]
|
||||||
if errcode != RESET_ERROR:
|
if errcode != RESET_ERROR:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
class ConfigSocketReceiver(ThreadingTCPServer):
|
class ConfigSocketReceiver(ThreadingTCPServer):
|
||||||
"""
|
"""
|
||||||
A simple TCP socket-based logging config receiver.
|
A simple TCP socket-based logging config receiver.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
allow_reuse_address = 1
|
allow_reuse_address = 1
|
||||||
|
|
||||||
def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
|
def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
|
||||||
handler=None):
|
handler=None):
|
||||||
ThreadingTCPServer.__init__(self, (host, port), handler)
|
ThreadingTCPServer.__init__(self, (host, port), handler)
|
||||||
logging._acquireLock()
|
logging._acquireLock()
|
||||||
self.abort = 0
|
self.abort = 0
|
||||||
logging._releaseLock()
|
logging._releaseLock()
|
||||||
self.timeout = 1
|
self.timeout = 1
|
||||||
|
|
||||||
def serve_until_stopped(self):
|
def serve_until_stopped(self):
|
||||||
import select
|
import select
|
||||||
abort = 0
|
abort = 0
|
||||||
while not abort:
|
while not abort:
|
||||||
rd, wr, ex = select.select([self.socket.fileno()],
|
rd, wr, ex = select.select([self.socket.fileno()],
|
||||||
[], [],
|
[], [],
|
||||||
self.timeout)
|
self.timeout)
|
||||||
if rd:
|
if rd:
|
||||||
self.handle_request()
|
self.handle_request()
|
||||||
logging._acquireLock()
|
logging._acquireLock()
|
||||||
abort = self.abort
|
abort = self.abort
|
||||||
logging._releaseLock()
|
logging._releaseLock()
|
||||||
|
|
||||||
def serve(rcvr, hdlr, port):
|
def serve(rcvr, hdlr, port):
|
||||||
server = rcvr(port=port, handler=hdlr)
|
server = rcvr(port=port, handler=hdlr)
|
||||||
global _listener
|
global _listener
|
||||||
logging._acquireLock()
|
logging._acquireLock()
|
||||||
_listener = server
|
_listener = server
|
||||||
logging._releaseLock()
|
logging._releaseLock()
|
||||||
server.serve_until_stopped()
|
server.serve_until_stopped()
|
||||||
|
|
||||||
return threading.Thread(target=serve,
|
return threading.Thread(target=serve,
|
||||||
args=(ConfigSocketReceiver,
|
args=(ConfigSocketReceiver,
|
||||||
ConfigStreamHandler, port))
|
ConfigStreamHandler, port))
|
||||||
|
|
||||||
def stopListening():
|
def stopListening():
|
||||||
"""
|
"""
|
||||||
Stop the listening server which was created with a call to listen().
|
Stop the listening server which was created with a call to listen().
|
||||||
"""
|
"""
|
||||||
global _listener
|
global _listener
|
||||||
if _listener:
|
if _listener:
|
||||||
logging._acquireLock()
|
logging._acquireLock()
|
||||||
_listener.abort = 1
|
_listener.abort = 1
|
||||||
_listener = None
|
_listener = None
|
||||||
logging._releaseLock()
|
logging._releaseLock()
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
[run]
|
|
||||||
branch = True
|
|
||||||
|
|
||||||
[report]
|
|
||||||
exclude_lines =
|
|
||||||
pragma: no cover
|
|
||||||
def __repr__
|
|
||||||
except ImportError
|
|
||||||
omit =
|
|
||||||
tinycss/tests/speed.py
|
|
|
@ -1,12 +0,0 @@
|
||||||
*.pyc
|
|
||||||
*.c
|
|
||||||
*.so
|
|
||||||
*.egg-info
|
|
||||||
/.coverage
|
|
||||||
/htmlcov
|
|
||||||
/build
|
|
||||||
/dist
|
|
||||||
/.tox
|
|
||||||
/MANIFEST
|
|
||||||
/docs/_build
|
|
||||||
/env
|
|
|
@ -1,16 +0,0 @@
|
||||||
language: python
|
|
||||||
|
|
||||||
python:
|
|
||||||
- "2.7"
|
|
||||||
- "3.3"
|
|
||||||
- "3.4"
|
|
||||||
- "3.5"
|
|
||||||
- "pypy"
|
|
||||||
- "pypy3"
|
|
||||||
|
|
||||||
install:
|
|
||||||
- pip install Cython
|
|
||||||
- pip install --upgrade -e .[test]
|
|
||||||
|
|
||||||
script:
|
|
||||||
- python setup.py test
|
|
|
@ -1,53 +0,0 @@
|
||||||
tinycss changelog
|
|
||||||
=================
|
|
||||||
|
|
||||||
|
|
||||||
Version 0.4
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Released on 2016-09-23.
|
|
||||||
|
|
||||||
* Add an __eq__ operator to Token object.
|
|
||||||
* Support Fonts 3.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Version 0.3
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Released on 2012-09-18.
|
|
||||||
|
|
||||||
* Fix a bug when parsing \5c (an escaped antislash.)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Version 0.2
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Released on 2012-04-27.
|
|
||||||
|
|
||||||
**Breaking changes:**
|
|
||||||
|
|
||||||
* Remove the ``selectors3`` module. The functionality has moved to the
|
|
||||||
`cssselect <http://packages.python.org/cssselect/>`_ project.
|
|
||||||
* Simplify the API for :func:`~tinycss.make_parser`.
|
|
||||||
|
|
||||||
|
|
||||||
Version 0.1.1
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Released on 2012-04-06.
|
|
||||||
|
|
||||||
Bug fixes:
|
|
||||||
|
|
||||||
* Error handling on exepected end of stylesheet in an at-rule head
|
|
||||||
* Fix the installation on ASCII-only locales
|
|
||||||
|
|
||||||
|
|
||||||
Version 0.1
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Released on 2012-04-05.
|
|
||||||
|
|
||||||
First release. Parser support for CSS 2.1, Seloctors 3, Color 3 and
|
|
||||||
Paged Media 3.
|
|
|
@ -1,31 +0,0 @@
|
||||||
Copyright (c) 2012 by Simon Sapin.
|
|
||||||
|
|
||||||
Some rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use 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.
|
|
||||||
|
|
||||||
* The names of the contributors may not be used to endorse or
|
|
||||||
promote products derived from this software without specific
|
|
||||||
prior written permission.
|
|
||||||
|
|
||||||
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.
|
|
|
@ -1,3 +0,0 @@
|
||||||
include README.rst CHANGES LICENSE tox.ini .coveragerc tinycss/speedups.c
|
|
||||||
recursive-include docs *
|
|
||||||
prune docs/_build
|
|
|
@ -1,47 +0,0 @@
|
||||||
Metadata-Version: 1.1
|
|
||||||
Name: tinycss
|
|
||||||
Version: 0.4
|
|
||||||
Summary: tinycss is a complete yet simple CSS parser for Python.
|
|
||||||
Home-page: http://tinycss.readthedocs.io/
|
|
||||||
Author: Simon Sapin
|
|
||||||
Author-email: simon.sapin@exyr.org
|
|
||||||
License: BSD
|
|
||||||
Description: tinycss: CSS parser for Python
|
|
||||||
==============================
|
|
||||||
|
|
||||||
*tinycss* is a complete yet simple CSS parser for Python. It supports the full
|
|
||||||
syntax and error handling for CSS 2.1 as well as some CSS 3 modules:
|
|
||||||
|
|
||||||
* CSS Color 3
|
|
||||||
* CSS Fonts 3
|
|
||||||
* CSS Paged Media 3
|
|
||||||
|
|
||||||
It is designed to be easy to extend for new CSS modules and syntax,
|
|
||||||
and integrates well with cssselect_ for Selectors 3 support.
|
|
||||||
|
|
||||||
Quick facts:
|
|
||||||
|
|
||||||
* Free software: BSD licensed
|
|
||||||
* Compatible with Python 2.7 and 3.x
|
|
||||||
* Latest documentation `on python.org`_
|
|
||||||
* Source, issues and pull requests `on Github`_
|
|
||||||
* Releases `on PyPI`_
|
|
||||||
* Install with ``pip install tinycss``
|
|
||||||
|
|
||||||
.. _cssselect: http://packages.python.org/cssselect/
|
|
||||||
.. _on python.org: http://packages.python.org/tinycss/
|
|
||||||
.. _on Github: https://github.com/SimonSapin/tinycss/
|
|
||||||
.. _on PyPI: http://pypi.python.org/pypi/tinycss
|
|
||||||
|
|
||||||
Platform: UNKNOWN
|
|
||||||
Classifier: Development Status :: 4 - Beta
|
|
||||||
Classifier: Intended Audience :: Developers
|
|
||||||
Classifier: License :: OSI Approved :: BSD License
|
|
||||||
Classifier: Programming Language :: Python :: 2
|
|
||||||
Classifier: Programming Language :: Python :: 2.7
|
|
||||||
Classifier: Programming Language :: Python :: 3
|
|
||||||
Classifier: Programming Language :: Python :: 3.3
|
|
||||||
Classifier: Programming Language :: Python :: 3.4
|
|
||||||
Classifier: Programming Language :: Python :: 3.5
|
|
||||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
||||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
|
@ -1,26 +0,0 @@
|
||||||
tinycss: CSS parser for Python
|
|
||||||
==============================
|
|
||||||
|
|
||||||
*tinycss* is a complete yet simple CSS parser for Python. It supports the full
|
|
||||||
syntax and error handling for CSS 2.1 as well as some CSS 3 modules:
|
|
||||||
|
|
||||||
* CSS Color 3
|
|
||||||
* CSS Fonts 3
|
|
||||||
* CSS Paged Media 3
|
|
||||||
|
|
||||||
It is designed to be easy to extend for new CSS modules and syntax,
|
|
||||||
and integrates well with cssselect_ for Selectors 3 support.
|
|
||||||
|
|
||||||
Quick facts:
|
|
||||||
|
|
||||||
* Free software: BSD licensed
|
|
||||||
* Compatible with Python 2.7 and 3.x
|
|
||||||
* Latest documentation `on python.org`_
|
|
||||||
* Source, issues and pull requests `on Github`_
|
|
||||||
* Releases `on PyPI`_
|
|
||||||
* Install with ``pip install tinycss``
|
|
||||||
|
|
||||||
.. _cssselect: http://packages.python.org/cssselect/
|
|
||||||
.. _on python.org: http://packages.python.org/tinycss/
|
|
||||||
.. _on Github: https://github.com/SimonSapin/tinycss/
|
|
||||||
.. _on PyPI: http://pypi.python.org/pypi/tinycss
|
|
|
@ -1,24 +0,0 @@
|
||||||
div.body {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
div.document p, div.document ul {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
div.document ul ul {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: .5em;
|
|
||||||
}
|
|
||||||
.field-name {
|
|
||||||
padding-right: .5em;
|
|
||||||
}
|
|
||||||
table.field-list p, table.field-list ul {
|
|
||||||
margin-bottom: .5em;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
table.docutils td, table.docutils th {
|
|
||||||
padding: .2em .5em;
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
{% extends "!layout.html" %}
|
|
||||||
{% block extrahead %}
|
|
||||||
<link rel="stylesheet" href="{{ pathto('_static/custom.css', 1) }}" />
|
|
||||||
{% endblock %}
|
|
|
@ -1 +0,0 @@
|
||||||
.. include:: ../CHANGES
|
|
|
@ -1,252 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# tinycss documentation build configuration file, created by
|
|
||||||
# sphinx-quickstart on Tue Mar 27 14:20:34 2012.
|
|
||||||
#
|
|
||||||
# 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.insert(0, os.path.abspath('.'))
|
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
|
||||||
|
|
||||||
# If your documentation needs a minimal Sphinx version, state it here.
|
|
||||||
#needs_sphinx = '1.0'
|
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
|
||||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx',
|
|
||||||
'sphinx.ext.viewcode', 'sphinx.ext.doctest']
|
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
|
||||||
templates_path = ['_templates']
|
|
||||||
|
|
||||||
# The suffix of source filenames.
|
|
||||||
source_suffix = '.rst'
|
|
||||||
|
|
||||||
# The encoding of source files.
|
|
||||||
#source_encoding = 'utf-8-sig'
|
|
||||||
|
|
||||||
# The master toctree document.
|
|
||||||
master_doc = 'index'
|
|
||||||
|
|
||||||
# General information about the project.
|
|
||||||
project = 'tinycss'
|
|
||||||
copyright = '2012, Simon Sapin'
|
|
||||||
|
|
||||||
# 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 full version, including alpha/beta/rc tags.
|
|
||||||
#release = '0.1dev'
|
|
||||||
import re
|
|
||||||
with open(os.path.join(os.path.dirname(__file__), '..',
|
|
||||||
'tinycss', 'version.py')) as init_py:
|
|
||||||
release = re.search("VERSION = '([^']+)'", init_py.read()).group(1)
|
|
||||||
# The short X.Y version.
|
|
||||||
version = release.rstrip('dev')
|
|
||||||
|
|
||||||
# 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 patterns, relative to source directory, that match files and
|
|
||||||
# directories to ignore when looking for source files.
|
|
||||||
exclude_patterns = ['_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 = []
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output ---------------------------------------------------
|
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
|
||||||
# a list of builtin themes.
|
|
||||||
#html_theme = 'agogo'
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
#html_theme_options = {}
|
|
||||||
|
|
||||||
# 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_domain_indices = True
|
|
||||||
|
|
||||||
# If false, no index is generated.
|
|
||||||
#html_use_index = True
|
|
||||||
|
|
||||||
# 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 = True
|
|
||||||
|
|
||||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
|
||||||
#html_show_sphinx = True
|
|
||||||
|
|
||||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
|
||||||
#html_show_copyright = True
|
|
||||||
|
|
||||||
# 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 = ''
|
|
||||||
|
|
||||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
|
||||||
#html_file_suffix = None
|
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
|
||||||
htmlhelp_basename = 'tinycssdoc'
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output --------------------------------------------------
|
|
||||||
|
|
||||||
latex_elements = {
|
|
||||||
# The paper size ('letterpaper' or 'a4paper').
|
|
||||||
#'papersize': 'letterpaper',
|
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
|
||||||
#'pointsize': '10pt',
|
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
|
||||||
#'preamble': '',
|
|
||||||
}
|
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
|
||||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
|
||||||
latex_documents = [
|
|
||||||
('index', 'tinycss.tex', 'tinycss Documentation',
|
|
||||||
'Simon Sapin', '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
|
|
||||||
|
|
||||||
# If true, show page references after internal links.
|
|
||||||
#latex_show_pagerefs = False
|
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
|
||||||
#latex_show_urls = False
|
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
|
||||||
#latex_appendices = []
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
#latex_domain_indices = True
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for manual page output --------------------------------------------
|
|
||||||
|
|
||||||
# One entry per manual page. List of tuples
|
|
||||||
# (source start file, name, description, authors, manual section).
|
|
||||||
man_pages = [
|
|
||||||
('index', 'tinycss', 'tinycss Documentation',
|
|
||||||
['Simon Sapin'], 1)
|
|
||||||
]
|
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
|
||||||
#man_show_urls = False
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output ------------------------------------------------
|
|
||||||
|
|
||||||
# Grouping the document tree into Texinfo files. List of tuples
|
|
||||||
# (source start file, target name, title, author,
|
|
||||||
# dir menu entry, description, category)
|
|
||||||
texinfo_documents = [
|
|
||||||
('index', 'tinycss', 'tinycss Documentation',
|
|
||||||
'Simon Sapin', 'tinycss', 'One line description of project.',
|
|
||||||
'Miscellaneous'),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
|
||||||
#texinfo_appendices = []
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
#texinfo_domain_indices = True
|
|
||||||
|
|
||||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
|
||||||
#texinfo_show_urls = 'footnote'
|
|
||||||
|
|
||||||
|
|
||||||
# Example configuration for intersphinx: refer to the Python standard library.
|
|
||||||
intersphinx_mapping = {'http://docs.python.org/': None}
|
|
|
@ -1,116 +0,0 @@
|
||||||
CSS 3 Modules
|
|
||||||
=============
|
|
||||||
|
|
||||||
.. _selectors3:
|
|
||||||
|
|
||||||
Selectors 3
|
|
||||||
-----------
|
|
||||||
|
|
||||||
.. currentmodule:: tinycss.css21
|
|
||||||
|
|
||||||
On :attr:`RuleSet.selector`, the :meth:`~.token_data.TokenList.as_css` method
|
|
||||||
can be used to serialize a selector back to an Unicode string.
|
|
||||||
|
|
||||||
>>> import tinycss
|
|
||||||
>>> stylesheet = tinycss.make_parser().parse_stylesheet(
|
|
||||||
... 'div.error, #root > section:first-letter { color: red }')
|
|
||||||
>>> selector_string = stylesheet.rules[0].selector.as_css()
|
|
||||||
>>> selector_string
|
|
||||||
'div.error, #root > section:first-letter'
|
|
||||||
|
|
||||||
This string can be parsed by cssselect_. The parsed objects have information
|
|
||||||
about pseudo-elements and selector specificity.
|
|
||||||
|
|
||||||
.. _cssselect: http://packages.python.org/cssselect/
|
|
||||||
|
|
||||||
>>> import cssselect
|
|
||||||
>>> selectors = cssselect.parse(selector_string)
|
|
||||||
>>> [s.specificity() for s in selectors]
|
|
||||||
[(0, 1, 1), (1, 0, 2)]
|
|
||||||
>>> [s.pseudo_element for s in selectors]
|
|
||||||
[None, 'first-letter']
|
|
||||||
|
|
||||||
These objects can in turn be translated to XPath expressions. Note that
|
|
||||||
the translation ignores pseudo-elements, you have to account for them
|
|
||||||
somehow or reject selectors with pseudo-elements.
|
|
||||||
|
|
||||||
>>> xpath = cssselect.HTMLTranslator().selector_to_xpath(selectors[1])
|
|
||||||
>>> xpath
|
|
||||||
"descendant-or-self::*[@id = 'root']/section"
|
|
||||||
|
|
||||||
Finally, the XPath expressions can be used with lxml_ to find the matching
|
|
||||||
elements.
|
|
||||||
|
|
||||||
>>> from lxml import etree
|
|
||||||
>>> compiled_selector = etree.XPath(xpath)
|
|
||||||
>>> document = etree.fromstring('''<section id="root">
|
|
||||||
... <section id="head">Title</section>
|
|
||||||
... <section id="content">
|
|
||||||
... Lorem <section id="sub-section">ipsum</section>
|
|
||||||
... </section>
|
|
||||||
... </section>''')
|
|
||||||
>>> [el.get('id') for el in compiled_selector(document)]
|
|
||||||
['head', 'content']
|
|
||||||
|
|
||||||
.. _lxml: http://lxml.de/xpathxslt.html#xpath
|
|
||||||
|
|
||||||
Find more details in the `cssselect documentation`_.
|
|
||||||
|
|
||||||
.. _cssselect documentation: http://packages.python.org/cssselect/
|
|
||||||
|
|
||||||
|
|
||||||
.. module:: tinycss.color3
|
|
||||||
|
|
||||||
Color 3
|
|
||||||
-------
|
|
||||||
|
|
||||||
This module implements parsing for the *<color>* values, as defined in
|
|
||||||
`CSS 3 Color <http://www.w3.org/TR/css3-color/>`_.
|
|
||||||
|
|
||||||
The (deprecated) CSS2 system colors are not supported, but you can
|
|
||||||
easily test for them if you want as they are simple ``IDENT`` tokens.
|
|
||||||
For example::
|
|
||||||
|
|
||||||
if token.type == 'IDENT' and token.value == 'ButtonText':
|
|
||||||
return ...
|
|
||||||
|
|
||||||
All other values types *are* supported:
|
|
||||||
|
|
||||||
* Basic, extended (X11) and transparent color keywords;
|
|
||||||
* 3-digit and 6-digit hexadecimal notations;
|
|
||||||
* ``rgb()``, ``rgba()``, ``hsl()`` and ``hsla()`` functional notations.
|
|
||||||
* ``currentColor``
|
|
||||||
|
|
||||||
This module does not integrate with a parser class. Instead, it provides
|
|
||||||
a function that can parse tokens as found in :attr:`.css21.Declaration.value`,
|
|
||||||
for example.
|
|
||||||
|
|
||||||
.. autofunction:: parse_color
|
|
||||||
.. autofunction:: parse_color_string
|
|
||||||
.. autoclass:: RGBA
|
|
||||||
|
|
||||||
|
|
||||||
.. module:: tinycss.page3
|
|
||||||
|
|
||||||
Paged Media 3
|
|
||||||
-------------
|
|
||||||
|
|
||||||
.. autoclass:: CSSPage3Parser
|
|
||||||
.. autoclass:: MarginRule
|
|
||||||
|
|
||||||
|
|
||||||
.. module:: tinycss.fonts3
|
|
||||||
|
|
||||||
Fonts 3
|
|
||||||
-------
|
|
||||||
|
|
||||||
.. autoclass:: CSSFonts3Parser
|
|
||||||
.. autoclass:: FontFaceRule
|
|
||||||
.. autoclass:: FontFeatureValuesRule
|
|
||||||
.. autoclass:: FontFeatureRule
|
|
||||||
|
|
||||||
|
|
||||||
Other CSS modules
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
To add support for new CSS syntax, see :ref:`extending`.
|
|
|
@ -1,97 +0,0 @@
|
||||||
.. _extending:
|
|
||||||
|
|
||||||
Extending the parser
|
|
||||||
====================
|
|
||||||
|
|
||||||
Modules such as :mod:`.page3` extend the CSS 2.1 parser to add support for
|
|
||||||
CSS 3 syntax.
|
|
||||||
They do so by sub-classing :class:`.css21.CSS21Parser` and overriding/extending
|
|
||||||
some of its methods. If fact, the parser is made of methods in a class
|
|
||||||
(rather than a set of functions) solely to enable this kind of sub-classing.
|
|
||||||
|
|
||||||
tinycss is designed to enable you to have parser subclasses outside of
|
|
||||||
tinycss, without monkey-patching. If however the syntax you added is for a
|
|
||||||
W3C specification, consider including your subclass in a new tinycss module
|
|
||||||
and send a pull request: see :ref:`hacking`.
|
|
||||||
|
|
||||||
|
|
||||||
.. currentmodule:: tinycss.css21
|
|
||||||
|
|
||||||
Example: star hack
|
|
||||||
------------------
|
|
||||||
|
|
||||||
.. _star hack: https://en.wikipedia.org/wiki/CSS_filter#Star_hack
|
|
||||||
|
|
||||||
The `star hack`_ uses invalid declarations that are only parsed by some
|
|
||||||
versions of Internet Explorer. By default, tinycss ignores invalid
|
|
||||||
declarations and logs an error.
|
|
||||||
|
|
||||||
>>> from tinycss.css21 import CSS21Parser
|
|
||||||
>>> css = '#elem { width: [W3C Model Width]; *width: [BorderBox Model]; }'
|
|
||||||
>>> stylesheet = CSS21Parser().parse_stylesheet(css)
|
|
||||||
>>> stylesheet.errors
|
|
||||||
[ParseError('Parse error at 1:35, expected a property name, got DELIM',)]
|
|
||||||
>>> [decl.name for decl in stylesheet.rules[0].declarations]
|
|
||||||
['width']
|
|
||||||
|
|
||||||
If for example a minifier based on tinycss wants to support the star hack,
|
|
||||||
it can by extending the parser::
|
|
||||||
|
|
||||||
>>> class CSSStarHackParser(CSS21Parser):
|
|
||||||
... def parse_declaration(self, tokens):
|
|
||||||
... has_star_hack = (tokens[0].type == 'DELIM' and tokens[0].value == '*')
|
|
||||||
... if has_star_hack:
|
|
||||||
... tokens = tokens[1:]
|
|
||||||
... declaration = super(CSSStarHackParser, self).parse_declaration(tokens)
|
|
||||||
... declaration.has_star_hack = has_star_hack
|
|
||||||
... return declaration
|
|
||||||
...
|
|
||||||
>>> stylesheet = CSSStarHackParser().parse_stylesheet(css)
|
|
||||||
>>> stylesheet.errors
|
|
||||||
[]
|
|
||||||
>>> [(d.name, d.has_star_hack) for d in stylesheet.rules[0].declarations]
|
|
||||||
[('width', False), ('width', True)]
|
|
||||||
|
|
||||||
This class extends the :meth:`~CSS21Parser.parse_declaration` method.
|
|
||||||
It removes any ``*`` delimeter :class:`~.token_data.Token` at the start of
|
|
||||||
a declaration, and adds a ``has_star_hack`` boolean attribute on parsed
|
|
||||||
:class:`Declaration` objects: ``True`` if a ``*`` was removed, ``False`` for
|
|
||||||
“normal” declarations.
|
|
||||||
|
|
||||||
|
|
||||||
Parser methods
|
|
||||||
--------------
|
|
||||||
|
|
||||||
In addition to methods of the user API (see :ref:`parsing`), here
|
|
||||||
are the methods of the CSS 2.1 parser that can be overriden or extended:
|
|
||||||
|
|
||||||
.. automethod:: CSS21Parser.parse_rules
|
|
||||||
.. automethod:: CSS21Parser.read_at_rule
|
|
||||||
.. automethod:: CSS21Parser.parse_at_rule
|
|
||||||
.. automethod:: CSS21Parser.parse_media
|
|
||||||
.. automethod:: CSS21Parser.parse_page_selector
|
|
||||||
.. automethod:: CSS21Parser.parse_declarations_and_at_rules
|
|
||||||
.. automethod:: CSS21Parser.parse_ruleset
|
|
||||||
.. automethod:: CSS21Parser.parse_declaration_list
|
|
||||||
.. automethod:: CSS21Parser.parse_declaration
|
|
||||||
.. automethod:: CSS21Parser.parse_value_priority
|
|
||||||
|
|
||||||
Unparsed at-rules
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
.. autoclass:: AtRule
|
|
||||||
|
|
||||||
|
|
||||||
.. module:: tinycss.parsing
|
|
||||||
|
|
||||||
Parsing helper functions
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
The :mod:`tinycss.parsing` module contains helper functions for parsing
|
|
||||||
tokens into a more structured form:
|
|
||||||
|
|
||||||
.. autofunction:: strip_whitespace
|
|
||||||
.. autofunction:: split_on_comma
|
|
||||||
.. autofunction:: validate_value
|
|
||||||
.. autofunction:: validate_block
|
|
||||||
.. autofunction:: validate_any
|
|
|
@ -1,117 +0,0 @@
|
||||||
.. _hacking:
|
|
||||||
|
|
||||||
Hacking tinycss
|
|
||||||
===============
|
|
||||||
|
|
||||||
.. highlight:: sh
|
|
||||||
|
|
||||||
Bugs and feature requests
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Bug reports, feature requests and other issues should got to the
|
|
||||||
`tinycss issue tracker`_ on Github. Any suggestion or feedback is welcome.
|
|
||||||
Please include in full any error message, trackback or other detail that
|
|
||||||
could be helpful.
|
|
||||||
|
|
||||||
.. _tinycss issue tracker: https://github.com/SimonSapin/tinycss/issues
|
|
||||||
|
|
||||||
|
|
||||||
Installing the development version
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
First, get the latest git version::
|
|
||||||
|
|
||||||
git clone https://github.com/SimonSapin/tinycss.git
|
|
||||||
cd tinycss
|
|
||||||
|
|
||||||
You will need Cython_ and pytest_. Installing in a virtualenv_ is recommended::
|
|
||||||
|
|
||||||
virtualenv env
|
|
||||||
. env/bin/activate
|
|
||||||
pip install Cython pytest
|
|
||||||
|
|
||||||
.. _Cython: http://cython.org/
|
|
||||||
.. _pytest: http://pytest.org/
|
|
||||||
.. _virtualenv: http://www.virtualenv.org/
|
|
||||||
|
|
||||||
Then, install tinycss in-place with pip’s *editable mode*. This will also
|
|
||||||
build the accelerators::
|
|
||||||
|
|
||||||
pip install -e .
|
|
||||||
|
|
||||||
|
|
||||||
Running the test suite
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Once you have everything installed (see above), just run pytest from the
|
|
||||||
*tinycss* directory::
|
|
||||||
|
|
||||||
py.test
|
|
||||||
|
|
||||||
If the accelerators are not available for some reason, use the
|
|
||||||
``TINYCSS_SKIP_SPEEDUPS_TESTS`` environment variable::
|
|
||||||
|
|
||||||
TINYCSS_SKIP_SPEEDUPS_TESTS=1 py.test
|
|
||||||
|
|
||||||
If you get test failures on a fresh git clone, something may have gone wrong
|
|
||||||
during the installation. Otherwise, you probably found a bug. Please
|
|
||||||
`report it <#bugs-and-feature-requests>`_.
|
|
||||||
|
|
||||||
|
|
||||||
Test in multiple Python versions with tox
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
tox_ automatically creates virtualenvs for various Python versions and
|
|
||||||
runs the test suite there::
|
|
||||||
|
|
||||||
pip install tox
|
|
||||||
|
|
||||||
Change to the project’s root directory and just run::
|
|
||||||
|
|
||||||
tox
|
|
||||||
|
|
||||||
.. _tox: http://tox.testrun.org/
|
|
||||||
|
|
||||||
tinycss comes with a pre-configured ``tox.ini`` file to test in CPython
|
|
||||||
2.6, 2.7, 3.1 and 3.2 as well as PyPy. You can change that with the ``-e``
|
|
||||||
parameter::
|
|
||||||
|
|
||||||
tox -e py27,py32
|
|
||||||
|
|
||||||
If you use ``--`` in the arguments passed to tox, further arguments
|
|
||||||
are passed to the underlying ``py.test`` command::
|
|
||||||
|
|
||||||
tox -- -x --pdb
|
|
||||||
|
|
||||||
|
|
||||||
Building the documentation
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
This documentation is made with Sphinx_::
|
|
||||||
|
|
||||||
pip install Sphinx
|
|
||||||
|
|
||||||
.. _Sphinx: http://sphinx.pocoo.org/
|
|
||||||
|
|
||||||
To build the HTML version of the documentation, change to the project’s root
|
|
||||||
directory and run::
|
|
||||||
|
|
||||||
python setup.py build_sphinx
|
|
||||||
|
|
||||||
The built HTML files are in ``docs/_build/html``.
|
|
||||||
|
|
||||||
|
|
||||||
Making a patch and a pull request
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
If you would like to see something included in tinycss, please fork
|
|
||||||
`the repository <https://github.com/SimonSapin/tinycss/>`_ on Github
|
|
||||||
and make a pull request. Make sure to include tests for your change.
|
|
||||||
|
|
||||||
|
|
||||||
Mailing-list
|
|
||||||
------------
|
|
||||||
|
|
||||||
tinycss does not have a mailing-list of its own for now, but the
|
|
||||||
`WeasyPrint mailing-list <http://weasyprint.org/community/>`_
|
|
||||||
is appropriate to discuss it.
|
|
|
@ -1,50 +0,0 @@
|
||||||
.. include:: ../README.rst
|
|
||||||
|
|
||||||
|
|
||||||
Requirements
|
|
||||||
------------
|
|
||||||
|
|
||||||
`tinycss is tested <https://travis-ci.org/Kozea/tinycss>`_ on CPython 2.7, 3.3,
|
|
||||||
3.4 and 3.5 as well as PyPy 5.3 and PyPy3 2.4; it should work on any
|
|
||||||
implementation of **Python 2.7 or later version (including 3.x)** of the
|
|
||||||
language.
|
|
||||||
|
|
||||||
Cython_ is used for optional accelerators but is only required for
|
|
||||||
development versions on tinycss.
|
|
||||||
|
|
||||||
.. _Cython: http://cython.org/
|
|
||||||
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
Installing with `pip <http://www.pip-installer.org/>`_ should Just Work:
|
|
||||||
|
|
||||||
.. code-block:: sh
|
|
||||||
|
|
||||||
pip install tinycss
|
|
||||||
|
|
||||||
The release tarballs contain pre-*cythoned* C files for the accelerators:
|
|
||||||
you will not need Cython to install like this.
|
|
||||||
If the accelerators fail to build for some reason, tinycss will
|
|
||||||
print a warning and fall back to a pure-Python installation.
|
|
||||||
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
-------------
|
|
||||||
|
|
||||||
.. Have this page in the sidebar, but do not show a link to itself here:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:hidden:
|
|
||||||
|
|
||||||
self
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
parsing
|
|
||||||
css3
|
|
||||||
extending
|
|
||||||
hacking
|
|
||||||
changelog
|
|
|
@ -1,97 +0,0 @@
|
||||||
Parsing with tinycss
|
|
||||||
====================
|
|
||||||
|
|
||||||
.. highlight:: python
|
|
||||||
|
|
||||||
Quickstart
|
|
||||||
----------
|
|
||||||
|
|
||||||
Import *tinycss*, make a parser object with the features you want,
|
|
||||||
and parse a stylesheet:
|
|
||||||
|
|
||||||
.. doctest::
|
|
||||||
|
|
||||||
>>> import tinycss
|
|
||||||
>>> parser = tinycss.make_parser('page3')
|
|
||||||
>>> stylesheet = parser.parse_stylesheet_bytes(b'''@import "foo.css";
|
|
||||||
... p.error { color: red } @lorem-ipsum;
|
|
||||||
... @page tables { size: landscape }''')
|
|
||||||
>>> stylesheet.rules
|
|
||||||
[<ImportRule 1:1 foo.css>, <RuleSet at 2:5 p.error>, <PageRule 3:5 ('tables', None)>]
|
|
||||||
>>> stylesheet.errors
|
|
||||||
[ParseError('Parse error at 2:29, unknown at-rule in stylesheet context: @lorem-ipsum',)]
|
|
||||||
|
|
||||||
You’ll get a :class:`~tinycss.css21.Stylesheet` object which contains
|
|
||||||
all the parsed content as well as a list of encountered errors.
|
|
||||||
|
|
||||||
|
|
||||||
Parsers
|
|
||||||
-------
|
|
||||||
|
|
||||||
Parsers are subclasses of :class:`tinycss.css21.CSS21Parser`. Various
|
|
||||||
subclasses add support for more syntax. You can choose which features to
|
|
||||||
enable by making a new parser class with multiple inheritance, but there
|
|
||||||
is also a convenience function to do that:
|
|
||||||
|
|
||||||
.. module:: tinycss
|
|
||||||
|
|
||||||
.. autofunction:: make_parser
|
|
||||||
|
|
||||||
|
|
||||||
.. module:: tinycss.css21
|
|
||||||
.. _parsing:
|
|
||||||
|
|
||||||
Parsing a stylesheet
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Parser classes have three different methods to parse CSS stylesheet,
|
|
||||||
depending on whether you have a file, a byte string, or an Unicode string.
|
|
||||||
|
|
||||||
.. autoclass:: CSS21Parser
|
|
||||||
:members: parse_stylesheet_file, parse_stylesheet_bytes, parse_stylesheet
|
|
||||||
|
|
||||||
|
|
||||||
Parsing a ``style`` attribute
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
.. automethod:: CSS21Parser.parse_style_attr
|
|
||||||
|
|
||||||
|
|
||||||
Parsed objects
|
|
||||||
--------------
|
|
||||||
|
|
||||||
These data structures make up the results of the various parsing methods.
|
|
||||||
|
|
||||||
.. autoclass:: tinycss.parsing.ParseError()
|
|
||||||
.. autoclass:: Stylesheet()
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
All subsequent objects have :obj:`line` and :obj:`column` attributes (not
|
|
||||||
repeated every time fore brevity) that indicate where in the CSS source
|
|
||||||
this object was read.
|
|
||||||
|
|
||||||
.. autoclass:: RuleSet()
|
|
||||||
.. autoclass:: ImportRule()
|
|
||||||
.. autoclass:: MediaRule()
|
|
||||||
.. autoclass:: PageRule()
|
|
||||||
.. autoclass:: Declaration()
|
|
||||||
|
|
||||||
|
|
||||||
Tokens
|
|
||||||
------
|
|
||||||
|
|
||||||
Some parts of a stylesheet (such as selectors in CSS 2.1 or property values)
|
|
||||||
are not parsed by tinycss. They appear as tokens instead.
|
|
||||||
|
|
||||||
.. module:: tinycss.token_data
|
|
||||||
|
|
||||||
.. autoclass:: TokenList()
|
|
||||||
:member-order: bysource
|
|
||||||
:members:
|
|
||||||
.. autoclass:: Token()
|
|
||||||
:members:
|
|
||||||
.. autoclass:: tinycss.speedups.CToken()
|
|
||||||
.. autoclass:: ContainerToken()
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: FunctionToken()
|
|
|
@ -1,23 +0,0 @@
|
||||||
[build_sphinx]
|
|
||||||
source-dir = docs
|
|
||||||
build-dir = docs/_build
|
|
||||||
|
|
||||||
[upload_sphinx]
|
|
||||||
upload-dir = docs/_build/html
|
|
||||||
|
|
||||||
[aliases]
|
|
||||||
test = pytest
|
|
||||||
|
|
||||||
[tool:pytest]
|
|
||||||
addopts = --flake8 --isort --cov --ignore=test/cairosvg_reference
|
|
||||||
norecursedirs = dist .cache .git build *.egg-info .eggs venv cairosvg_reference
|
|
||||||
flake8-ignore = docs/conf.py ALL
|
|
||||||
isort_ignore =
|
|
||||||
docs/conf.py
|
|
||||||
setup.py
|
|
||||||
|
|
||||||
[egg_info]
|
|
||||||
tag_build =
|
|
||||||
tag_date = 0
|
|
||||||
tag_svn_revision = 0
|
|
||||||
|
|
|
@ -1,127 +0,0 @@
|
||||||
import os.path
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from distutils.errors import (
|
|
||||||
CCompilerError, DistutilsExecError, DistutilsPlatformError)
|
|
||||||
from setuptools import Extension, setup
|
|
||||||
|
|
||||||
try:
|
|
||||||
from Cython.Distutils import build_ext
|
|
||||||
import Cython.Compiler.Version
|
|
||||||
CYTHON_INSTALLED = True
|
|
||||||
except ImportError:
|
|
||||||
from distutils.command.build_ext import build_ext
|
|
||||||
CYTHON_INSTALLED = False
|
|
||||||
|
|
||||||
|
|
||||||
ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)
|
|
||||||
if sys.platform == 'win32' and sys.version_info > (2, 6):
|
|
||||||
# 2.6's distutils.msvc9compiler can raise an IOError when failing to
|
|
||||||
# find the compiler
|
|
||||||
ext_errors += (IOError,)
|
|
||||||
|
|
||||||
|
|
||||||
class BuildFailed(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ve_build_ext(build_ext):
|
|
||||||
# This class allows C extension building to fail.
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
try:
|
|
||||||
build_ext.run(self)
|
|
||||||
except DistutilsPlatformError:
|
|
||||||
raise BuildFailed
|
|
||||||
|
|
||||||
def build_extension(self, ext):
|
|
||||||
try:
|
|
||||||
build_ext.build_extension(self, ext)
|
|
||||||
except ext_errors:
|
|
||||||
raise BuildFailed
|
|
||||||
|
|
||||||
|
|
||||||
ROOT = os.path.dirname(__file__)
|
|
||||||
with open(os.path.join(ROOT, 'tinycss', 'version.py')) as fd:
|
|
||||||
VERSION = re.search("VERSION = '([^']+)'", fd.read()).group(1)
|
|
||||||
|
|
||||||
with open(os.path.join(ROOT, 'README.rst'), 'rb') as fd:
|
|
||||||
README = fd.read().decode('utf8')
|
|
||||||
|
|
||||||
|
|
||||||
needs_pytest = {'pytest', 'test', 'ptr'}.intersection(sys.argv)
|
|
||||||
pytest_runner = ['pytest-runner'] if needs_pytest else []
|
|
||||||
|
|
||||||
|
|
||||||
def run_setup(with_extension):
|
|
||||||
if with_extension:
|
|
||||||
extension_path = os.path.join('tinycss', 'speedups')
|
|
||||||
if CYTHON_INSTALLED:
|
|
||||||
extension_path += '.pyx'
|
|
||||||
print('Building with Cython %s.' % Cython.Compiler.Version.version)
|
|
||||||
else:
|
|
||||||
extension_path += '.c'
|
|
||||||
if not os.path.exists(extension_path):
|
|
||||||
print("WARNING: Trying to build without Cython, but "
|
|
||||||
"pre-generated '%s' does not seem to be available."
|
|
||||||
% extension_path)
|
|
||||||
else:
|
|
||||||
print('Building without Cython.')
|
|
||||||
kwargs = dict(
|
|
||||||
cmdclass=dict(build_ext=ve_build_ext),
|
|
||||||
ext_modules=[Extension('tinycss.speedups',
|
|
||||||
sources=[extension_path])],
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
kwargs = dict()
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name='tinycss',
|
|
||||||
version=VERSION,
|
|
||||||
url='http://tinycss.readthedocs.io/',
|
|
||||||
license='BSD',
|
|
||||||
author='Simon Sapin',
|
|
||||||
author_email='simon.sapin@exyr.org',
|
|
||||||
description='tinycss is a complete yet simple CSS parser for Python.',
|
|
||||||
long_description=README,
|
|
||||||
classifiers=[
|
|
||||||
'Development Status :: 4 - Beta',
|
|
||||||
'Intended Audience :: Developers',
|
|
||||||
'License :: OSI Approved :: BSD License',
|
|
||||||
'Programming Language :: Python :: 2',
|
|
||||||
'Programming Language :: Python :: 2.7',
|
|
||||||
'Programming Language :: Python :: 3',
|
|
||||||
'Programming Language :: Python :: 3.3',
|
|
||||||
'Programming Language :: Python :: 3.4',
|
|
||||||
'Programming Language :: Python :: 3.5',
|
|
||||||
'Programming Language :: Python :: Implementation :: CPython',
|
|
||||||
'Programming Language :: Python :: Implementation :: PyPy',
|
|
||||||
],
|
|
||||||
setup_requires=pytest_runner,
|
|
||||||
tests_require=[
|
|
||||||
'pytest-cov', 'pytest-flake8', 'pytest-isort', 'pytest-runner'],
|
|
||||||
extras_require={'test': (
|
|
||||||
'pytest-runner', 'pytest-cov', 'pytest-flake8', 'pytest-isort')},
|
|
||||||
packages=['tinycss', 'tinycss.tests'],
|
|
||||||
**kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
IS_PYPY = hasattr(sys, 'pypy_translation_info')
|
|
||||||
try:
|
|
||||||
run_setup(not IS_PYPY)
|
|
||||||
except BuildFailed:
|
|
||||||
BUILD_EXT_WARNING = ('WARNING: The extension could not be compiled, '
|
|
||||||
'speedups are not enabled.')
|
|
||||||
print('*' * 75)
|
|
||||||
print(BUILD_EXT_WARNING)
|
|
||||||
print('Failure information, if any, is above.')
|
|
||||||
print('Retrying the build without the Cython extension now.')
|
|
||||||
print('*' * 75)
|
|
||||||
|
|
||||||
run_setup(False)
|
|
||||||
|
|
||||||
print('*' * 75)
|
|
||||||
print(BUILD_EXT_WARNING)
|
|
||||||
print('Plain-Python installation succeeded.')
|
|
||||||
print('*' * 75)
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,5 +6,5 @@
|
||||||
"tetra"
|
"tetra"
|
||||||
],
|
],
|
||||||
"title": "Portal Auth",
|
"title": "Portal Auth",
|
||||||
"version": "1.7"
|
"version": "1.8"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue