Version 1.8

pull/34/head
sud0nick 2018-07-26 23:36:37 -04:00
parent 4d96f79d78
commit 299d105b22
45 changed files with 9227 additions and 2747 deletions

View File

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

View File

@ -0,0 +1,3 @@
July 26, 2018
<br /><br />
- Fixed tinycss lib reference<br />

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,10 +0,0 @@
[run]
branch = True
[report]
exclude_lines =
pragma: no cover
def __repr__
except ImportError
omit =
tinycss/tests/speed.py

View File

@ -1,12 +0,0 @@
*.pyc
*.c
*.so
*.egg-info
/.coverage
/htmlcov
/build
/dist
/.tox
/MANIFEST
/docs/_build
/env

View File

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

View File

@ -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.

View File

@ -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.

View File

@ -1,3 +0,0 @@
include README.rst CHANGES LICENSE tox.ini .coveragerc tinycss/speedups.c
recursive-include docs *
prune docs/_build

View File

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

View File

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

View File

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

View File

@ -1,4 +0,0 @@
{% extends "!layout.html" %}
{% block extrahead %}
<link rel="stylesheet" href="{{ pathto('_static/custom.css', 1) }}" />
{% endblock %}

View File

@ -1 +0,0 @@
.. include:: ../CHANGES

View File

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

View File

@ -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`.

View File

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

View File

@ -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 pips *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 projects 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 projects 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.

View File

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

View File

@ -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',)]
Youll 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()

View File

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

View File

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

View File

@ -6,5 +6,5 @@
"tetra" "tetra"
], ],
"title": "Portal Auth", "title": "Portal Auth",
"version": "1.7" "version": "1.8"
} }