supression de venv

This commit is contained in:
2024-07-14 13:37:19 +02:00
parent ce29392c15
commit 5653ed38a6
313 changed files with 0 additions and 85364 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +0,0 @@
Copyright (c) Django Software Foundation and individual contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. 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.
3. Neither the name of Django nor the names of its contributors may 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,290 +0,0 @@
Django is licensed under the three-clause BSD license; see the file
LICENSE for details.
Django includes code from the Python standard library, which is licensed under
the Python license, a permissive open source license. The copyright and license
is included below for compliance with Python's terms.
----------------------------------------------------------------------
Copyright (c) 2001-present Python Software Foundation; All Rights Reserved
A. HISTORY OF THE SOFTWARE
==========================
Python was created in the early 1990s by Guido van Rossum at Stichting
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
as a successor of a language called ABC. Guido remains Python's
principal author, although it includes many contributions from others.
In 1995, Guido continued his work on Python at the Corporation for
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
in Reston, Virginia where he released several versions of the
software.
In May 2000, Guido and the Python core development team moved to
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
year, the PythonLabs team moved to Digital Creations, which became
Zope Corporation. In 2001, the Python Software Foundation (PSF, see
https://www.python.org/psf/) was formed, a non-profit organization
created specifically to own Python-related Intellectual Property.
Zope Corporation was a sponsoring member of the PSF.
All Python releases are Open Source (see http://www.opensource.org for
the Open Source Definition). Historically, most, but not all, Python
releases have also been GPL-compatible; the table below summarizes
the various releases.
Release Derived Year Owner GPL-
from compatible? (1)
0.9.0 thru 1.2 1991-1995 CWI yes
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
1.6 1.5.2 2000 CNRI no
2.0 1.6 2000 BeOpen.com no
1.6.1 1.6 2001 CNRI yes (2)
2.1 2.0+1.6.1 2001 PSF no
2.0.1 2.0+1.6.1 2001 PSF yes
2.1.1 2.1+2.0.1 2001 PSF yes
2.1.2 2.1.1 2002 PSF yes
2.1.3 2.1.2 2002 PSF yes
2.2 and above 2.1.1 2001-now PSF yes
Footnotes:
(1) GPL-compatible doesn't mean that we're distributing Python under
the GPL. All Python licenses, unlike the GPL, let you distribute
a modified version without making your changes open source. The
GPL-compatible licenses make it possible to combine Python with
other software that is released under the GPL; the others don't.
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
because its license has a choice of law clause. According to
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
is "not incompatible" with the GPL.
Thanks to the many outside volunteers who have worked under Guido's
direction to make these releases possible.
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
===============================================================
Python software and documentation are licensed under the
Python Software Foundation License Version 2.
Starting with Python 3.8.6, examples, recipes, and other code in
the documentation are dual licensed under the PSF License Version 2
and the Zero-Clause BSD license.
Some software incorporated into Python is under different licenses.
The licenses are listed with code falling under that license.
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
--------------------------------------------
1. This LICENSE AGREEMENT is between the Python Software Foundation
("PSF"), and the Individual or Organization ("Licensee") accessing and
otherwise using this software ("Python") in source or binary form and
its associated documentation.
2. Subject to the terms and conditions of this License Agreement, PSF hereby
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
analyze, test, perform and/or display publicly, prepare derivative works,
distribute, and otherwise use Python alone or in any derivative version,
provided, however, that PSF's License Agreement and PSF's notice of copyright,
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Python Software Foundation;
All Rights Reserved" are retained in Python alone or in any derivative version
prepared by Licensee.
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python.
4. PSF is making Python available to Licensee on an "AS IS"
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between PSF and
Licensee. This License Agreement does not grant permission to use PSF
trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.
8. By copying, installing or otherwise using Python, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
-------------------------------------------
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
Individual or Organization ("Licensee") accessing and otherwise using
this software in source or binary form and its associated
documentation ("the Software").
2. Subject to the terms and conditions of this BeOpen Python License
Agreement, BeOpen hereby grants Licensee a non-exclusive,
royalty-free, world-wide license to reproduce, analyze, test, perform
and/or display publicly, prepare derivative works, distribute, and
otherwise use the Software alone or in any derivative version,
provided, however, that the BeOpen Python License is retained in the
Software, alone or in any derivative version prepared by Licensee.
3. BeOpen is making the Software available to Licensee on an "AS IS"
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
5. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
6. This License Agreement shall be governed by and interpreted in all
respects by the law of the State of California, excluding conflict of
law provisions. Nothing in this License Agreement shall be deemed to
create any relationship of agency, partnership, or joint venture
between BeOpen and Licensee. This License Agreement does not grant
permission to use BeOpen trademarks or trade names in a trademark
sense to endorse or promote products or services of Licensee, or any
third party. As an exception, the "BeOpen Python" logos available at
http://www.pythonlabs.com/logos.html may be used according to the
permissions granted on that web page.
7. By copying, installing or otherwise using the software, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
---------------------------------------
1. This LICENSE AGREEMENT is between the Corporation for National
Research Initiatives, having an office at 1895 Preston White Drive,
Reston, VA 20191 ("CNRI"), and the Individual or Organization
("Licensee") accessing and otherwise using Python 1.6.1 software in
source or binary form and its associated documentation.
2. Subject to the terms and conditions of this License Agreement, CNRI
hereby grants Licensee a nonexclusive, royalty-free, world-wide
license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use Python 1.6.1
alone or in any derivative version, provided, however, that CNRI's
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
1995-2001 Corporation for National Research Initiatives; All Rights
Reserved" are retained in Python 1.6.1 alone or in any derivative
version prepared by Licensee. Alternately, in lieu of CNRI's License
Agreement, Licensee may substitute the following text (omitting the
quotes): "Python 1.6.1 is made available subject to the terms and
conditions in CNRI's License Agreement. This Agreement together with
Python 1.6.1 may be located on the internet using the following
unique, persistent identifier (known as a handle): 1895.22/1013. This
Agreement may also be obtained from a proxy server on the internet
using the following URL: http://hdl.handle.net/1895.22/1013".
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python 1.6.1 or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python 1.6.1.
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. This License Agreement shall be governed by the federal
intellectual property law of the United States, including without
limitation the federal copyright law, and, to the extent such
U.S. federal law does not apply, by the law of the Commonwealth of
Virginia, excluding Virginia's conflict of law provisions.
Notwithstanding the foregoing, with regard to derivative works based
on Python 1.6.1 that incorporate non-separable material that was
previously distributed under the GNU General Public License (GPL), the
law of the Commonwealth of Virginia shall govern this License
Agreement only as to issues arising under or with respect to
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
License Agreement shall be deemed to create any relationship of
agency, partnership, or joint venture between CNRI and Licensee. This
License Agreement does not grant permission to use CNRI trademarks or
trade name in a trademark sense to endorse or promote products or
services of Licensee, or any third party.
8. By clicking on the "ACCEPT" button where indicated, or by copying,
installing or otherwise using Python 1.6.1, Licensee agrees to be
bound by the terms and conditions of this License Agreement.
ACCEPT
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
--------------------------------------------------
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
The Netherlands. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior
permission.
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
----------------------------------------------------------------------
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

View File

@@ -1,97 +0,0 @@
Metadata-Version: 2.1
Name: Django
Version: 4.0
Summary: A high-level Python web framework that encourages rapid development and clean, pragmatic design.
Home-page: https://www.djangoproject.com/
Author: Django Software Foundation
Author-email: foundation@djangoproject.com
License: BSD-3-Clause
Project-URL: Documentation, https://docs.djangoproject.com/
Project-URL: Release notes, https://docs.djangoproject.com/en/stable/releases/
Project-URL: Funding, https://www.djangoproject.com/fundraising/
Project-URL: Source, https://github.com/django/django
Project-URL: Tracker, https://code.djangoproject.com/
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Requires-Dist: asgiref (<4,>=3.4.1)
Requires-Dist: sqlparse (>=0.2.2)
Requires-Dist: backports.zoneinfo ; python_version < "3.9"
Requires-Dist: tzdata ; sys_platform == "win32"
Provides-Extra: argon2
Requires-Dist: argon2-cffi (>=19.1.0) ; extra == 'argon2'
Provides-Extra: bcrypt
Requires-Dist: bcrypt ; extra == 'bcrypt'
======
Django
======
Django is a high-level Python web framework that encourages rapid development
and clean, pragmatic design. Thanks for checking it out.
All documentation is in the "``docs``" directory and online at
https://docs.djangoproject.com/en/stable/. If you're just getting started,
here's how we recommend you read the docs:
* First, read ``docs/intro/install.txt`` for instructions on installing Django.
* Next, work through the tutorials in order (``docs/intro/tutorial01.txt``,
``docs/intro/tutorial02.txt``, etc.).
* If you want to set up an actual deployment server, read
``docs/howto/deployment/index.txt`` for instructions.
* You'll probably want to read through the topical guides (in ``docs/topics``)
next; from there you can jump to the HOWTOs (in ``docs/howto``) for specific
problems, and check out the reference (``docs/ref``) for gory details.
* See ``docs/README`` for instructions on building an HTML version of the docs.
Docs are updated rigorously. If you find any problems in the docs, or think
they should be clarified in any way, please take 30 seconds to fill out a
ticket here: https://code.djangoproject.com/newticket
To get more help:
* Join the ``#django`` channel on ``irc.libera.chat``. Lots of helpful people
hang out there. See https://web.libera.chat if you're new to IRC.
* Join the django-users mailing list, or read the archives, at
https://groups.google.com/group/django-users.
To contribute to Django:
* Check out https://docs.djangoproject.com/en/dev/internals/contributing/ for
information about getting involved.
To run Django's test suite:
* Follow the instructions in the "Unit tests" section of
``docs/internals/contributing/writing-code/unit-tests.txt``, published online at
https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/unit-tests/#running-the-unit-tests
Supporting the Development of Django
====================================
Django's development depends on your contributions.
If you depend on Django, remember to support the Django Software Foundation: https://www.djangoproject.com/fundraising/

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +0,0 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.34.2)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -1,3 +0,0 @@
[console_scripts]
django-admin = django.core.management:execute_from_command_line

View File

@@ -1,170 +0,0 @@
"""
MySQLdb - A DB API v2.0 compatible interface to MySQL.
This package is a wrapper around _mysql, which mostly implements the
MySQL C API.
connect() -- connects to server
See the C API specification and the MySQL documentation for more info
on other items.
For information on how MySQLdb handles type conversion, see the
MySQLdb.converters module.
"""
try:
from MySQLdb.release import version_info
from . import _mysql
assert version_info == _mysql.version_info
except Exception:
raise ImportError(
"this is MySQLdb version {}, but _mysql is version {!r}\n_mysql: {!r}".format(
version_info, _mysql.version_info, _mysql.__file__
)
)
from ._mysql import (
NotSupportedError,
OperationalError,
get_client_info,
ProgrammingError,
Error,
InterfaceError,
debug,
IntegrityError,
string_literal,
MySQLError,
DataError,
DatabaseError,
InternalError,
Warning,
)
from MySQLdb.constants import FIELD_TYPE
from MySQLdb.times import (
Date,
Time,
Timestamp,
DateFromTicks,
TimeFromTicks,
TimestampFromTicks,
)
threadsafety = 1
apilevel = "2.0"
paramstyle = "format"
class DBAPISet(frozenset):
"""A special type of set for which A == x is true if A is a
DBAPISet and x is a member of that set."""
def __eq__(self, other):
if isinstance(other, DBAPISet):
return not self.difference(other)
return other in self
STRING = DBAPISet([FIELD_TYPE.ENUM, FIELD_TYPE.STRING, FIELD_TYPE.VAR_STRING])
BINARY = DBAPISet(
[
FIELD_TYPE.BLOB,
FIELD_TYPE.LONG_BLOB,
FIELD_TYPE.MEDIUM_BLOB,
FIELD_TYPE.TINY_BLOB,
]
)
NUMBER = DBAPISet(
[
FIELD_TYPE.DECIMAL,
FIELD_TYPE.DOUBLE,
FIELD_TYPE.FLOAT,
FIELD_TYPE.INT24,
FIELD_TYPE.LONG,
FIELD_TYPE.LONGLONG,
FIELD_TYPE.TINY,
FIELD_TYPE.YEAR,
FIELD_TYPE.NEWDECIMAL,
]
)
DATE = DBAPISet([FIELD_TYPE.DATE])
TIME = DBAPISet([FIELD_TYPE.TIME])
TIMESTAMP = DBAPISet([FIELD_TYPE.TIMESTAMP, FIELD_TYPE.DATETIME])
DATETIME = TIMESTAMP
ROWID = DBAPISet()
def test_DBAPISet_set_equality():
assert STRING == STRING
def test_DBAPISet_set_inequality():
assert STRING != NUMBER
def test_DBAPISet_set_equality_membership():
assert FIELD_TYPE.VAR_STRING == STRING
def test_DBAPISet_set_inequality_membership():
assert FIELD_TYPE.DATE != STRING
def Binary(x):
return bytes(x)
def Connect(*args, **kwargs):
"""Factory function for connections.Connection."""
from MySQLdb.connections import Connection
return Connection(*args, **kwargs)
connect = Connection = Connect
__all__ = [
"BINARY",
"Binary",
"Connect",
"Connection",
"DATE",
"Date",
"Time",
"Timestamp",
"DateFromTicks",
"TimeFromTicks",
"TimestampFromTicks",
"DataError",
"DatabaseError",
"Error",
"FIELD_TYPE",
"IntegrityError",
"InterfaceError",
"InternalError",
"MySQLError",
"NUMBER",
"NotSupportedError",
"DBAPISet",
"OperationalError",
"ProgrammingError",
"ROWID",
"STRING",
"TIME",
"TIMESTAMP",
"Warning",
"apilevel",
"connect",
"connections",
"constants",
"converters",
"cursors",
"debug",
"get_client_info",
"paramstyle",
"string_literal",
"threadsafety",
"version_info",
]

View File

@@ -1,69 +0,0 @@
"""Exception classes for _mysql and MySQLdb.
These classes are dictated by the DB API v2.0:
https://www.python.org/dev/peps/pep-0249/
"""
class MySQLError(Exception):
"""Exception related to operation with MySQL."""
class Warning(Warning, MySQLError):
"""Exception raised for important warnings like data truncations
while inserting, etc."""
class Error(MySQLError):
"""Exception that is the base class of all other error exceptions
(not Warning)."""
class InterfaceError(Error):
"""Exception raised for errors that are related to the database
interface rather than the database itself."""
class DatabaseError(Error):
"""Exception raised for errors that are related to the
database."""
class DataError(DatabaseError):
"""Exception raised for errors that are due to problems with the
processed data like division by zero, numeric value out of range,
etc."""
class OperationalError(DatabaseError):
"""Exception raised for errors that are related to the database's
operation and not necessarily under the control of the programmer,
e.g. an unexpected disconnect occurs, the data source name is not
found, a transaction could not be processed, a memory allocation
error occurred during processing, etc."""
class IntegrityError(DatabaseError):
"""Exception raised when the relational integrity of the database
is affected, e.g. a foreign key check fails, duplicate key,
etc."""
class InternalError(DatabaseError):
"""Exception raised when the database encounters an internal
error, e.g. the cursor is not valid anymore, the transaction is
out of sync, etc."""
class ProgrammingError(DatabaseError):
"""Exception raised for programming errors, e.g. table not found
or already exists, syntax error in the SQL statement, wrong number
of parameters specified, etc."""
class NotSupportedError(DatabaseError):
"""Exception raised in case a method or database API was used
which is not supported by the database, e.g. requesting a
.rollback() on a connection that does not support transaction or
has transactions turned off."""

View File

@@ -1,333 +0,0 @@
"""
This module implements connections for MySQLdb. Presently there is
only one class: Connection. Others are unlikely. However, you might
want to make your own subclasses. In most cases, you will probably
override Connection.default_cursor with a non-standard Cursor class.
"""
import re
from . import cursors, _mysql
from ._exceptions import (
Warning,
Error,
InterfaceError,
DataError,
DatabaseError,
OperationalError,
IntegrityError,
InternalError,
NotSupportedError,
ProgrammingError,
)
# Mapping from MySQL charset name to Python codec name
_charset_to_encoding = {
"utf8mb4": "utf8",
"utf8mb3": "utf8",
"latin1": "cp1252",
"koi8r": "koi8_r",
"koi8u": "koi8_u",
}
re_numeric_part = re.compile(r"^(\d+)")
def numeric_part(s):
"""Returns the leading numeric part of a string.
>>> numeric_part("20-alpha")
20
>>> numeric_part("foo")
>>> numeric_part("16b")
16
"""
m = re_numeric_part.match(s)
if m:
return int(m.group(1))
return None
class Connection(_mysql.connection):
"""MySQL Database Connection Object"""
default_cursor = cursors.Cursor
def __init__(self, *args, **kwargs):
"""
Create a connection to the database. It is strongly recommended
that you only use keyword parameters. Consult the MySQL C API
documentation for more information.
:param str host: host to connect
:param str user: user to connect as
:param str password: password to use
:param str passwd: alias of password (deprecated)
:param str database: database to use
:param str db: alias of database (deprecated)
:param int port: TCP/IP port to connect to
:param str unix_socket: location of unix_socket to use
:param dict conv: conversion dictionary, see MySQLdb.converters
:param int connect_timeout:
number of seconds to wait before the connection attempt fails.
:param bool compress: if set, compression is enabled
:param str named_pipe: if set, a named pipe is used to connect (Windows only)
:param str init_command:
command which is run once the connection is created
:param str read_default_file:
file from which default client values are read
:param str read_default_group:
configuration group to use from the default file
:param type cursorclass:
class object, used to create cursors (keyword only)
:param bool use_unicode:
If True, text-like columns are returned as unicode objects
using the connection's character set. Otherwise, text-like
columns are returned as bytes. Unicode objects will always
be encoded to the connection's character set regardless of
this setting.
Default to True.
:param str charset:
If supplied, the connection character set will be changed
to this character set.
:param str auth_plugin:
If supplied, the connection default authentication plugin will be
changed to this value. Example values:
`mysql_native_password` or `caching_sha2_password`
:param str sql_mode:
If supplied, the session SQL mode will be changed to this
setting.
For more details and legal values, see the MySQL documentation.
:param int client_flag:
flags to use or 0 (see MySQL docs or constants/CLIENTS.py)
:param bool multi_statements:
If True, enable multi statements for clients >= 4.1.
Defaults to True.
:param str ssl_mode:
specify the security settings for connection to the server;
see the MySQL documentation for more details
(mysql_option(), MYSQL_OPT_SSL_MODE).
Only one of 'DISABLED', 'PREFERRED', 'REQUIRED',
'VERIFY_CA', 'VERIFY_IDENTITY' can be specified.
:param dict ssl:
dictionary or mapping contains SSL connection parameters;
see the MySQL documentation for more details
(mysql_ssl_set()). If this is set, and the client does not
support SSL, NotSupportedError will be raised.
:param bool local_infile:
enables LOAD LOCAL INFILE; zero disables
:param bool autocommit:
If False (default), autocommit is disabled.
If True, autocommit is enabled.
If None, autocommit isn't set and server default is used.
:param bool binary_prefix:
If set, the '_binary' prefix will be used for raw byte query
arguments (e.g. Binary). This is disabled by default.
There are a number of undocumented, non-standard methods. See the
documentation for the MySQL C API for some hints on what they do.
"""
from MySQLdb.constants import CLIENT, FIELD_TYPE
from MySQLdb.converters import conversions, _bytes_or_str
from weakref import proxy
kwargs2 = kwargs.copy()
if "db" in kwargs2:
kwargs2["database"] = kwargs2.pop("db")
if "passwd" in kwargs2:
kwargs2["password"] = kwargs2.pop("passwd")
if "conv" in kwargs:
conv = kwargs["conv"]
else:
conv = conversions
conv2 = {}
for k, v in conv.items():
if isinstance(k, int) and isinstance(v, list):
conv2[k] = v[:]
else:
conv2[k] = v
kwargs2["conv"] = conv2
cursorclass = kwargs2.pop("cursorclass", self.default_cursor)
charset = kwargs2.get("charset", "")
use_unicode = kwargs2.pop("use_unicode", True)
sql_mode = kwargs2.pop("sql_mode", "")
self._binary_prefix = kwargs2.pop("binary_prefix", False)
client_flag = kwargs.get("client_flag", 0)
client_flag |= CLIENT.MULTI_RESULTS
multi_statements = kwargs2.pop("multi_statements", True)
if multi_statements:
client_flag |= CLIENT.MULTI_STATEMENTS
kwargs2["client_flag"] = client_flag
# PEP-249 requires autocommit to be initially off
autocommit = kwargs2.pop("autocommit", False)
super().__init__(*args, **kwargs2)
self.cursorclass = cursorclass
self.encoders = {k: v for k, v in conv.items() if type(k) is not int}
self._server_version = tuple(
[numeric_part(n) for n in self.get_server_info().split(".")[:2]]
)
self.encoding = "ascii" # overridden in set_character_set()
if not charset:
charset = self.character_set_name()
self.set_character_set(charset)
if sql_mode:
self.set_sql_mode(sql_mode)
if use_unicode:
for t in (
FIELD_TYPE.STRING,
FIELD_TYPE.VAR_STRING,
FIELD_TYPE.VARCHAR,
FIELD_TYPE.TINY_BLOB,
FIELD_TYPE.MEDIUM_BLOB,
FIELD_TYPE.LONG_BLOB,
FIELD_TYPE.BLOB,
):
self.converter[t] = _bytes_or_str
# Unlike other string/blob types, JSON is always text.
# MySQL may return JSON with charset==binary.
self.converter[FIELD_TYPE.JSON] = str
db = proxy(self)
def unicode_literal(u, dummy=None):
return db.string_literal(u.encode(db.encoding))
self.encoders[str] = unicode_literal
self._transactional = self.server_capabilities & CLIENT.TRANSACTIONS
if self._transactional:
if autocommit is not None:
self.autocommit(autocommit)
self.messages = []
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
def autocommit(self, on):
on = bool(on)
if self.get_autocommit() != on:
_mysql.connection.autocommit(self, on)
def cursor(self, cursorclass=None):
"""
Create a cursor on which queries may be performed. The
optional cursorclass parameter is used to create the
Cursor. By default, self.cursorclass=cursors.Cursor is
used.
"""
return (cursorclass or self.cursorclass)(self)
def query(self, query):
# Since _mysql releases GIL while querying, we need immutable buffer.
if isinstance(query, bytearray):
query = bytes(query)
_mysql.connection.query(self, query)
def _bytes_literal(self, bs):
assert isinstance(bs, (bytes, bytearray))
x = self.string_literal(bs) # x is escaped and quoted bytes
if self._binary_prefix:
return b"_binary" + x
return x
def _tuple_literal(self, t):
return b"(%s)" % (b",".join(map(self.literal, t)))
def literal(self, o):
"""If o is a single object, returns an SQL literal as a string.
If o is a non-string sequence, the items of the sequence are
converted and returned as a sequence.
Non-standard. For internal use; do not use this in your
applications.
"""
if isinstance(o, str):
s = self.string_literal(o.encode(self.encoding))
elif isinstance(o, bytearray):
s = self._bytes_literal(o)
elif isinstance(o, bytes):
s = self._bytes_literal(o)
elif isinstance(o, (tuple, list)):
s = self._tuple_literal(o)
else:
s = self.escape(o, self.encoders)
if isinstance(s, str):
s = s.encode(self.encoding)
assert isinstance(s, bytes)
return s
def begin(self):
"""Explicitly begin a connection.
This method is not used when autocommit=False (default).
"""
self.query(b"BEGIN")
def set_character_set(self, charset):
"""Set the connection character set to charset."""
super().set_character_set(charset)
self.encoding = _charset_to_encoding.get(charset, charset)
def set_sql_mode(self, sql_mode):
"""Set the connection sql_mode. See MySQL documentation for
legal values."""
if self._server_version < (4, 1):
raise NotSupportedError("server is too old to set sql_mode")
self.query("SET SESSION sql_mode='%s'" % sql_mode)
self.store_result()
def show_warnings(self):
"""Return detailed information about warnings as a
sequence of tuples of (Level, Code, Message). This
is only supported in MySQL-4.1 and up. If your server
is an earlier version, an empty sequence is returned."""
if self._server_version < (4, 1):
return ()
self.query("SHOW WARNINGS")
r = self.store_result()
warnings = r.fetch_row(0)
return warnings
Warning = Warning
Error = Error
InterfaceError = InterfaceError
DatabaseError = DatabaseError
DataError = DataError
OperationalError = OperationalError
IntegrityError = IntegrityError
InternalError = InternalError
ProgrammingError = ProgrammingError
NotSupportedError = NotSupportedError
# vim: colorcolumn=100

View File

@@ -1,27 +0,0 @@
"""MySQL CLIENT constants
These constants are used when creating the connection. Use bitwise-OR
(|) to combine options together, and pass them as the client_flags
parameter to MySQLdb.Connection. For more information on these flags,
see the MySQL C API documentation for mysql_real_connect().
"""
LONG_PASSWORD = 1
FOUND_ROWS = 2
LONG_FLAG = 4
CONNECT_WITH_DB = 8
NO_SCHEMA = 16
COMPRESS = 32
ODBC = 64
LOCAL_FILES = 128
IGNORE_SPACE = 256
CHANGE_USER = 512
INTERACTIVE = 1024
SSL = 2048
IGNORE_SIGPIPE = 4096
TRANSACTIONS = 8192 # mysql_com.h was WRONG prior to 3.23.35
RESERVED = 16384
SECURE_CONNECTION = 32768
MULTI_STATEMENTS = 65536
MULTI_RESULTS = 131072

View File

@@ -1,105 +0,0 @@
"""MySQL Connection Errors
Nearly all of these raise OperationalError. COMMANDS_OUT_OF_SYNC
raises ProgrammingError.
"""
if __name__ == "__main__":
"""
Usage: python CR.py [/path/to/mysql/errmsg.h ...] >> CR.py
"""
import fileinput
import re
data = {}
error_last = None
for line in fileinput.input():
line = re.sub(r"/\*.*?\*/", "", line)
m = re.match(r"^\s*#define\s+CR_([A-Z0-9_]+)\s+(\d+)(\s.*|$)", line)
if m:
name = m.group(1)
value = int(m.group(2))
if name == "ERROR_LAST":
if error_last is None or error_last < value:
error_last = value
continue
if value not in data:
data[value] = set()
data[value].add(name)
for value, names in sorted(data.items()):
for name in sorted(names):
print("{} = {}".format(name, value))
if error_last is not None:
print("ERROR_LAST = %s" % error_last)
ERROR_FIRST = 2000
MIN_ERROR = 2000
UNKNOWN_ERROR = 2000
SOCKET_CREATE_ERROR = 2001
CONNECTION_ERROR = 2002
CONN_HOST_ERROR = 2003
IPSOCK_ERROR = 2004
UNKNOWN_HOST = 2005
SERVER_GONE_ERROR = 2006
VERSION_ERROR = 2007
OUT_OF_MEMORY = 2008
WRONG_HOST_INFO = 2009
LOCALHOST_CONNECTION = 2010
TCP_CONNECTION = 2011
SERVER_HANDSHAKE_ERR = 2012
SERVER_LOST = 2013
COMMANDS_OUT_OF_SYNC = 2014
NAMEDPIPE_CONNECTION = 2015
NAMEDPIPEWAIT_ERROR = 2016
NAMEDPIPEOPEN_ERROR = 2017
NAMEDPIPESETSTATE_ERROR = 2018
CANT_READ_CHARSET = 2019
NET_PACKET_TOO_LARGE = 2020
EMBEDDED_CONNECTION = 2021
PROBE_SLAVE_STATUS = 2022
PROBE_SLAVE_HOSTS = 2023
PROBE_SLAVE_CONNECT = 2024
PROBE_MASTER_CONNECT = 2025
SSL_CONNECTION_ERROR = 2026
MALFORMED_PACKET = 2027
WRONG_LICENSE = 2028
NULL_POINTER = 2029
NO_PREPARE_STMT = 2030
PARAMS_NOT_BOUND = 2031
DATA_TRUNCATED = 2032
NO_PARAMETERS_EXISTS = 2033
INVALID_PARAMETER_NO = 2034
INVALID_BUFFER_USE = 2035
UNSUPPORTED_PARAM_TYPE = 2036
SHARED_MEMORY_CONNECTION = 2037
SHARED_MEMORY_CONNECT_REQUEST_ERROR = 2038
SHARED_MEMORY_CONNECT_ANSWER_ERROR = 2039
SHARED_MEMORY_CONNECT_FILE_MAP_ERROR = 2040
SHARED_MEMORY_CONNECT_MAP_ERROR = 2041
SHARED_MEMORY_FILE_MAP_ERROR = 2042
SHARED_MEMORY_MAP_ERROR = 2043
SHARED_MEMORY_EVENT_ERROR = 2044
SHARED_MEMORY_CONNECT_ABANDONED_ERROR = 2045
SHARED_MEMORY_CONNECT_SET_ERROR = 2046
CONN_UNKNOW_PROTOCOL = 2047
INVALID_CONN_HANDLE = 2048
UNUSED_1 = 2049
FETCH_CANCELED = 2050
NO_DATA = 2051
NO_STMT_METADATA = 2052
NO_RESULT_SET = 2053
NOT_IMPLEMENTED = 2054
SERVER_LOST_EXTENDED = 2055
STMT_CLOSED = 2056
NEW_STMT_METADATA = 2057
ALREADY_CONNECTED = 2058
AUTH_PLUGIN_CANNOT_LOAD = 2059
DUPLICATE_CONNECTION_ATTR = 2060
AUTH_PLUGIN_ERR = 2061
INSECURE_API_ERR = 2062
FILE_NAME_TOO_LONG = 2063
SSL_FIPS_MODE_ERR = 2064
MAX_ERROR = 2999
ERROR_LAST = 2064

View File

@@ -1,827 +0,0 @@
"""MySQL ER Constants
These constants are error codes for the bulk of the error conditions
that may occur.
"""
if __name__ == "__main__":
"""
Usage: python ER.py [/path/to/mysql/mysqld_error.h ...] >> ER.py
"""
import fileinput
import re
data = {}
error_last = None
for line in fileinput.input():
line = re.sub(r"/\*.*?\*/", "", line)
m = re.match(r"^\s*#define\s+((ER|WARN)_[A-Z0-9_]+)\s+(\d+)\s*", line)
if m:
name = m.group(1)
if name.startswith("ER_"):
name = name[3:]
value = int(m.group(3))
if name == "ERROR_LAST":
if error_last is None or error_last < value:
error_last = value
continue
if value not in data:
data[value] = set()
data[value].add(name)
for value, names in sorted(data.items()):
for name in sorted(names):
print("{} = {}".format(name, value))
if error_last is not None:
print("ERROR_LAST = %s" % error_last)
ERROR_FIRST = 1000
NO = 1002
YES = 1003
CANT_CREATE_FILE = 1004
CANT_CREATE_TABLE = 1005
CANT_CREATE_DB = 1006
DB_CREATE_EXISTS = 1007
DB_DROP_EXISTS = 1008
DB_DROP_RMDIR = 1010
CANT_FIND_SYSTEM_REC = 1012
CANT_GET_STAT = 1013
CANT_LOCK = 1015
CANT_OPEN_FILE = 1016
FILE_NOT_FOUND = 1017
CANT_READ_DIR = 1018
CHECKREAD = 1020
DUP_KEY = 1022
ERROR_ON_READ = 1024
ERROR_ON_RENAME = 1025
ERROR_ON_WRITE = 1026
FILE_USED = 1027
FILSORT_ABORT = 1028
GET_ERRNO = 1030
ILLEGAL_HA = 1031
KEY_NOT_FOUND = 1032
NOT_FORM_FILE = 1033
NOT_KEYFILE = 1034
OLD_KEYFILE = 1035
OPEN_AS_READONLY = 1036
OUTOFMEMORY = 1037
OUT_OF_SORTMEMORY = 1038
CON_COUNT_ERROR = 1040
OUT_OF_RESOURCES = 1041
BAD_HOST_ERROR = 1042
HANDSHAKE_ERROR = 1043
DBACCESS_DENIED_ERROR = 1044
ACCESS_DENIED_ERROR = 1045
NO_DB_ERROR = 1046
UNKNOWN_COM_ERROR = 1047
BAD_NULL_ERROR = 1048
BAD_DB_ERROR = 1049
TABLE_EXISTS_ERROR = 1050
BAD_TABLE_ERROR = 1051
NON_UNIQ_ERROR = 1052
SERVER_SHUTDOWN = 1053
BAD_FIELD_ERROR = 1054
WRONG_FIELD_WITH_GROUP = 1055
WRONG_GROUP_FIELD = 1056
WRONG_SUM_SELECT = 1057
WRONG_VALUE_COUNT = 1058
TOO_LONG_IDENT = 1059
DUP_FIELDNAME = 1060
DUP_KEYNAME = 1061
DUP_ENTRY = 1062
WRONG_FIELD_SPEC = 1063
PARSE_ERROR = 1064
EMPTY_QUERY = 1065
NONUNIQ_TABLE = 1066
INVALID_DEFAULT = 1067
MULTIPLE_PRI_KEY = 1068
TOO_MANY_KEYS = 1069
TOO_MANY_KEY_PARTS = 1070
TOO_LONG_KEY = 1071
KEY_COLUMN_DOES_NOT_EXITS = 1072
BLOB_USED_AS_KEY = 1073
TOO_BIG_FIELDLENGTH = 1074
WRONG_AUTO_KEY = 1075
READY = 1076
SHUTDOWN_COMPLETE = 1079
FORCING_CLOSE = 1080
IPSOCK_ERROR = 1081
NO_SUCH_INDEX = 1082
WRONG_FIELD_TERMINATORS = 1083
BLOBS_AND_NO_TERMINATED = 1084
TEXTFILE_NOT_READABLE = 1085
FILE_EXISTS_ERROR = 1086
LOAD_INFO = 1087
ALTER_INFO = 1088
WRONG_SUB_KEY = 1089
CANT_REMOVE_ALL_FIELDS = 1090
CANT_DROP_FIELD_OR_KEY = 1091
INSERT_INFO = 1092
UPDATE_TABLE_USED = 1093
NO_SUCH_THREAD = 1094
KILL_DENIED_ERROR = 1095
NO_TABLES_USED = 1096
TOO_BIG_SET = 1097
NO_UNIQUE_LOGFILE = 1098
TABLE_NOT_LOCKED_FOR_WRITE = 1099
TABLE_NOT_LOCKED = 1100
BLOB_CANT_HAVE_DEFAULT = 1101
WRONG_DB_NAME = 1102
WRONG_TABLE_NAME = 1103
TOO_BIG_SELECT = 1104
UNKNOWN_ERROR = 1105
UNKNOWN_PROCEDURE = 1106
WRONG_PARAMCOUNT_TO_PROCEDURE = 1107
WRONG_PARAMETERS_TO_PROCEDURE = 1108
UNKNOWN_TABLE = 1109
FIELD_SPECIFIED_TWICE = 1110
INVALID_GROUP_FUNC_USE = 1111
UNSUPPORTED_EXTENSION = 1112
TABLE_MUST_HAVE_COLUMNS = 1113
RECORD_FILE_FULL = 1114
UNKNOWN_CHARACTER_SET = 1115
TOO_MANY_TABLES = 1116
TOO_MANY_FIELDS = 1117
TOO_BIG_ROWSIZE = 1118
STACK_OVERRUN = 1119
WRONG_OUTER_JOIN_UNUSED = 1120
NULL_COLUMN_IN_INDEX = 1121
CANT_FIND_UDF = 1122
CANT_INITIALIZE_UDF = 1123
UDF_NO_PATHS = 1124
UDF_EXISTS = 1125
CANT_OPEN_LIBRARY = 1126
CANT_FIND_DL_ENTRY = 1127
FUNCTION_NOT_DEFINED = 1128
HOST_IS_BLOCKED = 1129
HOST_NOT_PRIVILEGED = 1130
PASSWORD_ANONYMOUS_USER = 1131
PASSWORD_NOT_ALLOWED = 1132
PASSWORD_NO_MATCH = 1133
UPDATE_INFO = 1134
CANT_CREATE_THREAD = 1135
WRONG_VALUE_COUNT_ON_ROW = 1136
CANT_REOPEN_TABLE = 1137
INVALID_USE_OF_NULL = 1138
REGEXP_ERROR = 1139
MIX_OF_GROUP_FUNC_AND_FIELDS = 1140
NONEXISTING_GRANT = 1141
TABLEACCESS_DENIED_ERROR = 1142
COLUMNACCESS_DENIED_ERROR = 1143
ILLEGAL_GRANT_FOR_TABLE = 1144
GRANT_WRONG_HOST_OR_USER = 1145
NO_SUCH_TABLE = 1146
NONEXISTING_TABLE_GRANT = 1147
NOT_ALLOWED_COMMAND = 1148
SYNTAX_ERROR = 1149
ABORTING_CONNECTION = 1152
NET_PACKET_TOO_LARGE = 1153
NET_READ_ERROR_FROM_PIPE = 1154
NET_FCNTL_ERROR = 1155
NET_PACKETS_OUT_OF_ORDER = 1156
NET_UNCOMPRESS_ERROR = 1157
NET_READ_ERROR = 1158
NET_READ_INTERRUPTED = 1159
NET_ERROR_ON_WRITE = 1160
NET_WRITE_INTERRUPTED = 1161
TOO_LONG_STRING = 1162
TABLE_CANT_HANDLE_BLOB = 1163
TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164
WRONG_COLUMN_NAME = 1166
WRONG_KEY_COLUMN = 1167
WRONG_MRG_TABLE = 1168
DUP_UNIQUE = 1169
BLOB_KEY_WITHOUT_LENGTH = 1170
PRIMARY_CANT_HAVE_NULL = 1171
TOO_MANY_ROWS = 1172
REQUIRES_PRIMARY_KEY = 1173
UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175
KEY_DOES_NOT_EXITS = 1176
CHECK_NO_SUCH_TABLE = 1177
CHECK_NOT_IMPLEMENTED = 1178
CANT_DO_THIS_DURING_AN_TRANSACTION = 1179
ERROR_DURING_COMMIT = 1180
ERROR_DURING_ROLLBACK = 1181
ERROR_DURING_FLUSH_LOGS = 1182
NEW_ABORTING_CONNECTION = 1184
MASTER = 1188
MASTER_NET_READ = 1189
MASTER_NET_WRITE = 1190
FT_MATCHING_KEY_NOT_FOUND = 1191
LOCK_OR_ACTIVE_TRANSACTION = 1192
UNKNOWN_SYSTEM_VARIABLE = 1193
CRASHED_ON_USAGE = 1194
CRASHED_ON_REPAIR = 1195
WARNING_NOT_COMPLETE_ROLLBACK = 1196
TRANS_CACHE_FULL = 1197
SLAVE_NOT_RUNNING = 1199
BAD_SLAVE = 1200
MASTER_INFO = 1201
SLAVE_THREAD = 1202
TOO_MANY_USER_CONNECTIONS = 1203
SET_CONSTANTS_ONLY = 1204
LOCK_WAIT_TIMEOUT = 1205
LOCK_TABLE_FULL = 1206
READ_ONLY_TRANSACTION = 1207
WRONG_ARGUMENTS = 1210
NO_PERMISSION_TO_CREATE_USER = 1211
LOCK_DEADLOCK = 1213
TABLE_CANT_HANDLE_FT = 1214
CANNOT_ADD_FOREIGN = 1215
NO_REFERENCED_ROW = 1216
ROW_IS_REFERENCED = 1217
CONNECT_TO_MASTER = 1218
ERROR_WHEN_EXECUTING_COMMAND = 1220
WRONG_USAGE = 1221
WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222
CANT_UPDATE_WITH_READLOCK = 1223
MIXING_NOT_ALLOWED = 1224
DUP_ARGUMENT = 1225
USER_LIMIT_REACHED = 1226
SPECIFIC_ACCESS_DENIED_ERROR = 1227
LOCAL_VARIABLE = 1228
GLOBAL_VARIABLE = 1229
NO_DEFAULT = 1230
WRONG_VALUE_FOR_VAR = 1231
WRONG_TYPE_FOR_VAR = 1232
VAR_CANT_BE_READ = 1233
CANT_USE_OPTION_HERE = 1234
NOT_SUPPORTED_YET = 1235
MASTER_FATAL_ERROR_READING_BINLOG = 1236
SLAVE_IGNORED_TABLE = 1237
INCORRECT_GLOBAL_LOCAL_VAR = 1238
WRONG_FK_DEF = 1239
KEY_REF_DO_NOT_MATCH_TABLE_REF = 1240
OPERAND_COLUMNS = 1241
SUBQUERY_NO_1_ROW = 1242
UNKNOWN_STMT_HANDLER = 1243
CORRUPT_HELP_DB = 1244
AUTO_CONVERT = 1246
ILLEGAL_REFERENCE = 1247
DERIVED_MUST_HAVE_ALIAS = 1248
SELECT_REDUCED = 1249
TABLENAME_NOT_ALLOWED_HERE = 1250
NOT_SUPPORTED_AUTH_MODE = 1251
SPATIAL_CANT_HAVE_NULL = 1252
COLLATION_CHARSET_MISMATCH = 1253
TOO_BIG_FOR_UNCOMPRESS = 1256
ZLIB_Z_MEM_ERROR = 1257
ZLIB_Z_BUF_ERROR = 1258
ZLIB_Z_DATA_ERROR = 1259
CUT_VALUE_GROUP_CONCAT = 1260
WARN_TOO_FEW_RECORDS = 1261
WARN_TOO_MANY_RECORDS = 1262
WARN_NULL_TO_NOTNULL = 1263
WARN_DATA_OUT_OF_RANGE = 1264
WARN_DATA_TRUNCATED = 1265
WARN_USING_OTHER_HANDLER = 1266
CANT_AGGREGATE_2COLLATIONS = 1267
REVOKE_GRANTS = 1269
CANT_AGGREGATE_3COLLATIONS = 1270
CANT_AGGREGATE_NCOLLATIONS = 1271
VARIABLE_IS_NOT_STRUCT = 1272
UNKNOWN_COLLATION = 1273
SLAVE_IGNORED_SSL_PARAMS = 1274
SERVER_IS_IN_SECURE_AUTH_MODE = 1275
WARN_FIELD_RESOLVED = 1276
BAD_SLAVE_UNTIL_COND = 1277
MISSING_SKIP_SLAVE = 1278
UNTIL_COND_IGNORED = 1279
WRONG_NAME_FOR_INDEX = 1280
WRONG_NAME_FOR_CATALOG = 1281
BAD_FT_COLUMN = 1283
UNKNOWN_KEY_CACHE = 1284
WARN_HOSTNAME_WONT_WORK = 1285
UNKNOWN_STORAGE_ENGINE = 1286
WARN_DEPRECATED_SYNTAX = 1287
NON_UPDATABLE_TABLE = 1288
FEATURE_DISABLED = 1289
OPTION_PREVENTS_STATEMENT = 1290
DUPLICATED_VALUE_IN_TYPE = 1291
TRUNCATED_WRONG_VALUE = 1292
INVALID_ON_UPDATE = 1294
UNSUPPORTED_PS = 1295
GET_ERRMSG = 1296
GET_TEMPORARY_ERRMSG = 1297
UNKNOWN_TIME_ZONE = 1298
WARN_INVALID_TIMESTAMP = 1299
INVALID_CHARACTER_STRING = 1300
WARN_ALLOWED_PACKET_OVERFLOWED = 1301
CONFLICTING_DECLARATIONS = 1302
SP_NO_RECURSIVE_CREATE = 1303
SP_ALREADY_EXISTS = 1304
SP_DOES_NOT_EXIST = 1305
SP_DROP_FAILED = 1306
SP_STORE_FAILED = 1307
SP_LILABEL_MISMATCH = 1308
SP_LABEL_REDEFINE = 1309
SP_LABEL_MISMATCH = 1310
SP_UNINIT_VAR = 1311
SP_BADSELECT = 1312
SP_BADRETURN = 1313
SP_BADSTATEMENT = 1314
UPDATE_LOG_DEPRECATED_IGNORED = 1315
UPDATE_LOG_DEPRECATED_TRANSLATED = 1316
QUERY_INTERRUPTED = 1317
SP_WRONG_NO_OF_ARGS = 1318
SP_COND_MISMATCH = 1319
SP_NORETURN = 1320
SP_NORETURNEND = 1321
SP_BAD_CURSOR_QUERY = 1322
SP_BAD_CURSOR_SELECT = 1323
SP_CURSOR_MISMATCH = 1324
SP_CURSOR_ALREADY_OPEN = 1325
SP_CURSOR_NOT_OPEN = 1326
SP_UNDECLARED_VAR = 1327
SP_WRONG_NO_OF_FETCH_ARGS = 1328
SP_FETCH_NO_DATA = 1329
SP_DUP_PARAM = 1330
SP_DUP_VAR = 1331
SP_DUP_COND = 1332
SP_DUP_CURS = 1333
SP_CANT_ALTER = 1334
SP_SUBSELECT_NYI = 1335
STMT_NOT_ALLOWED_IN_SF_OR_TRG = 1336
SP_VARCOND_AFTER_CURSHNDLR = 1337
SP_CURSOR_AFTER_HANDLER = 1338
SP_CASE_NOT_FOUND = 1339
FPARSER_TOO_BIG_FILE = 1340
FPARSER_BAD_HEADER = 1341
FPARSER_EOF_IN_COMMENT = 1342
FPARSER_ERROR_IN_PARAMETER = 1343
FPARSER_EOF_IN_UNKNOWN_PARAMETER = 1344
VIEW_NO_EXPLAIN = 1345
WRONG_OBJECT = 1347
NONUPDATEABLE_COLUMN = 1348
VIEW_SELECT_CLAUSE = 1350
VIEW_SELECT_VARIABLE = 1351
VIEW_SELECT_TMPTABLE = 1352
VIEW_WRONG_LIST = 1353
WARN_VIEW_MERGE = 1354
WARN_VIEW_WITHOUT_KEY = 1355
VIEW_INVALID = 1356
SP_NO_DROP_SP = 1357
TRG_ALREADY_EXISTS = 1359
TRG_DOES_NOT_EXIST = 1360
TRG_ON_VIEW_OR_TEMP_TABLE = 1361
TRG_CANT_CHANGE_ROW = 1362
TRG_NO_SUCH_ROW_IN_TRG = 1363
NO_DEFAULT_FOR_FIELD = 1364
DIVISION_BY_ZERO = 1365
TRUNCATED_WRONG_VALUE_FOR_FIELD = 1366
ILLEGAL_VALUE_FOR_TYPE = 1367
VIEW_NONUPD_CHECK = 1368
VIEW_CHECK_FAILED = 1369
PROCACCESS_DENIED_ERROR = 1370
RELAY_LOG_FAIL = 1371
UNKNOWN_TARGET_BINLOG = 1373
IO_ERR_LOG_INDEX_READ = 1374
BINLOG_PURGE_PROHIBITED = 1375
FSEEK_FAIL = 1376
BINLOG_PURGE_FATAL_ERR = 1377
LOG_IN_USE = 1378
LOG_PURGE_UNKNOWN_ERR = 1379
RELAY_LOG_INIT = 1380
NO_BINARY_LOGGING = 1381
RESERVED_SYNTAX = 1382
PS_MANY_PARAM = 1390
KEY_PART_0 = 1391
VIEW_CHECKSUM = 1392
VIEW_MULTIUPDATE = 1393
VIEW_NO_INSERT_FIELD_LIST = 1394
VIEW_DELETE_MERGE_VIEW = 1395
CANNOT_USER = 1396
XAER_NOTA = 1397
XAER_INVAL = 1398
XAER_RMFAIL = 1399
XAER_OUTSIDE = 1400
XAER_RMERR = 1401
XA_RBROLLBACK = 1402
NONEXISTING_PROC_GRANT = 1403
PROC_AUTO_GRANT_FAIL = 1404
PROC_AUTO_REVOKE_FAIL = 1405
DATA_TOO_LONG = 1406
SP_BAD_SQLSTATE = 1407
STARTUP = 1408
LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR = 1409
CANT_CREATE_USER_WITH_GRANT = 1410
WRONG_VALUE_FOR_TYPE = 1411
TABLE_DEF_CHANGED = 1412
SP_DUP_HANDLER = 1413
SP_NOT_VAR_ARG = 1414
SP_NO_RETSET = 1415
CANT_CREATE_GEOMETRY_OBJECT = 1416
BINLOG_UNSAFE_ROUTINE = 1418
BINLOG_CREATE_ROUTINE_NEED_SUPER = 1419
STMT_HAS_NO_OPEN_CURSOR = 1421
COMMIT_NOT_ALLOWED_IN_SF_OR_TRG = 1422
NO_DEFAULT_FOR_VIEW_FIELD = 1423
SP_NO_RECURSION = 1424
TOO_BIG_SCALE = 1425
TOO_BIG_PRECISION = 1426
M_BIGGER_THAN_D = 1427
WRONG_LOCK_OF_SYSTEM_TABLE = 1428
CONNECT_TO_FOREIGN_DATA_SOURCE = 1429
QUERY_ON_FOREIGN_DATA_SOURCE = 1430
FOREIGN_DATA_SOURCE_DOESNT_EXIST = 1431
FOREIGN_DATA_STRING_INVALID_CANT_CREATE = 1432
FOREIGN_DATA_STRING_INVALID = 1433
TRG_IN_WRONG_SCHEMA = 1435
STACK_OVERRUN_NEED_MORE = 1436
TOO_LONG_BODY = 1437
WARN_CANT_DROP_DEFAULT_KEYCACHE = 1438
TOO_BIG_DISPLAYWIDTH = 1439
XAER_DUPID = 1440
DATETIME_FUNCTION_OVERFLOW = 1441
CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG = 1442
VIEW_PREVENT_UPDATE = 1443
PS_NO_RECURSION = 1444
SP_CANT_SET_AUTOCOMMIT = 1445
VIEW_FRM_NO_USER = 1447
VIEW_OTHER_USER = 1448
NO_SUCH_USER = 1449
FORBID_SCHEMA_CHANGE = 1450
ROW_IS_REFERENCED_2 = 1451
NO_REFERENCED_ROW_2 = 1452
SP_BAD_VAR_SHADOW = 1453
TRG_NO_DEFINER = 1454
OLD_FILE_FORMAT = 1455
SP_RECURSION_LIMIT = 1456
SP_WRONG_NAME = 1458
TABLE_NEEDS_UPGRADE = 1459
SP_NO_AGGREGATE = 1460
MAX_PREPARED_STMT_COUNT_REACHED = 1461
VIEW_RECURSIVE = 1462
NON_GROUPING_FIELD_USED = 1463
TABLE_CANT_HANDLE_SPKEYS = 1464
NO_TRIGGERS_ON_SYSTEM_SCHEMA = 1465
REMOVED_SPACES = 1466
AUTOINC_READ_FAILED = 1467
USERNAME = 1468
HOSTNAME = 1469
WRONG_STRING_LENGTH = 1470
NON_INSERTABLE_TABLE = 1471
ADMIN_WRONG_MRG_TABLE = 1472
TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT = 1473
NAME_BECOMES_EMPTY = 1474
AMBIGUOUS_FIELD_TERM = 1475
FOREIGN_SERVER_EXISTS = 1476
FOREIGN_SERVER_DOESNT_EXIST = 1477
ILLEGAL_HA_CREATE_OPTION = 1478
PARTITION_REQUIRES_VALUES_ERROR = 1479
PARTITION_WRONG_VALUES_ERROR = 1480
PARTITION_MAXVALUE_ERROR = 1481
PARTITION_WRONG_NO_PART_ERROR = 1484
PARTITION_WRONG_NO_SUBPART_ERROR = 1485
WRONG_EXPR_IN_PARTITION_FUNC_ERROR = 1486
FIELD_NOT_FOUND_PART_ERROR = 1488
INCONSISTENT_PARTITION_INFO_ERROR = 1490
PARTITION_FUNC_NOT_ALLOWED_ERROR = 1491
PARTITIONS_MUST_BE_DEFINED_ERROR = 1492
RANGE_NOT_INCREASING_ERROR = 1493
INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR = 1494
MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR = 1495
PARTITION_ENTRY_ERROR = 1496
MIX_HANDLER_ERROR = 1497
PARTITION_NOT_DEFINED_ERROR = 1498
TOO_MANY_PARTITIONS_ERROR = 1499
SUBPARTITION_ERROR = 1500
CANT_CREATE_HANDLER_FILE = 1501
BLOB_FIELD_IN_PART_FUNC_ERROR = 1502
UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF = 1503
NO_PARTS_ERROR = 1504
PARTITION_MGMT_ON_NONPARTITIONED = 1505
FOREIGN_KEY_ON_PARTITIONED = 1506
DROP_PARTITION_NON_EXISTENT = 1507
DROP_LAST_PARTITION = 1508
COALESCE_ONLY_ON_HASH_PARTITION = 1509
REORG_HASH_ONLY_ON_SAME_NO = 1510
REORG_NO_PARAM_ERROR = 1511
ONLY_ON_RANGE_LIST_PARTITION = 1512
ADD_PARTITION_SUBPART_ERROR = 1513
ADD_PARTITION_NO_NEW_PARTITION = 1514
COALESCE_PARTITION_NO_PARTITION = 1515
REORG_PARTITION_NOT_EXIST = 1516
SAME_NAME_PARTITION = 1517
NO_BINLOG_ERROR = 1518
CONSECUTIVE_REORG_PARTITIONS = 1519
REORG_OUTSIDE_RANGE = 1520
PARTITION_FUNCTION_FAILURE = 1521
LIMITED_PART_RANGE = 1523
PLUGIN_IS_NOT_LOADED = 1524
WRONG_VALUE = 1525
NO_PARTITION_FOR_GIVEN_VALUE = 1526
FILEGROUP_OPTION_ONLY_ONCE = 1527
CREATE_FILEGROUP_FAILED = 1528
DROP_FILEGROUP_FAILED = 1529
TABLESPACE_AUTO_EXTEND_ERROR = 1530
WRONG_SIZE_NUMBER = 1531
SIZE_OVERFLOW_ERROR = 1532
ALTER_FILEGROUP_FAILED = 1533
BINLOG_ROW_LOGGING_FAILED = 1534
EVENT_ALREADY_EXISTS = 1537
EVENT_DOES_NOT_EXIST = 1539
EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG = 1542
EVENT_ENDS_BEFORE_STARTS = 1543
EVENT_EXEC_TIME_IN_THE_PAST = 1544
EVENT_SAME_NAME = 1551
DROP_INDEX_FK = 1553
WARN_DEPRECATED_SYNTAX_WITH_VER = 1554
CANT_LOCK_LOG_TABLE = 1556
FOREIGN_DUPLICATE_KEY_OLD_UNUSED = 1557
COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE = 1558
TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR = 1559
STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1560
PARTITION_NO_TEMPORARY = 1562
PARTITION_CONST_DOMAIN_ERROR = 1563
PARTITION_FUNCTION_IS_NOT_ALLOWED = 1564
NULL_IN_VALUES_LESS_THAN = 1566
WRONG_PARTITION_NAME = 1567
CANT_CHANGE_TX_CHARACTERISTICS = 1568
DUP_ENTRY_AUTOINCREMENT_CASE = 1569
EVENT_SET_VAR_ERROR = 1571
PARTITION_MERGE_ERROR = 1572
BASE64_DECODE_ERROR = 1575
EVENT_RECURSION_FORBIDDEN = 1576
ONLY_INTEGERS_ALLOWED = 1578
UNSUPORTED_LOG_ENGINE = 1579
BAD_LOG_STATEMENT = 1580
CANT_RENAME_LOG_TABLE = 1581
WRONG_PARAMCOUNT_TO_NATIVE_FCT = 1582
WRONG_PARAMETERS_TO_NATIVE_FCT = 1583
WRONG_PARAMETERS_TO_STORED_FCT = 1584
NATIVE_FCT_NAME_COLLISION = 1585
DUP_ENTRY_WITH_KEY_NAME = 1586
BINLOG_PURGE_EMFILE = 1587
EVENT_CANNOT_CREATE_IN_THE_PAST = 1588
EVENT_CANNOT_ALTER_IN_THE_PAST = 1589
NO_PARTITION_FOR_GIVEN_VALUE_SILENT = 1591
BINLOG_UNSAFE_STATEMENT = 1592
BINLOG_FATAL_ERROR = 1593
BINLOG_LOGGING_IMPOSSIBLE = 1598
VIEW_NO_CREATION_CTX = 1599
VIEW_INVALID_CREATION_CTX = 1600
TRG_CORRUPTED_FILE = 1602
TRG_NO_CREATION_CTX = 1603
TRG_INVALID_CREATION_CTX = 1604
EVENT_INVALID_CREATION_CTX = 1605
TRG_CANT_OPEN_TABLE = 1606
NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT = 1609
SLAVE_CORRUPT_EVENT = 1610
LOG_PURGE_NO_FILE = 1612
XA_RBTIMEOUT = 1613
XA_RBDEADLOCK = 1614
NEED_REPREPARE = 1615
WARN_NO_MASTER_INFO = 1617
WARN_OPTION_IGNORED = 1618
PLUGIN_DELETE_BUILTIN = 1619
WARN_PLUGIN_BUSY = 1620
VARIABLE_IS_READONLY = 1621
WARN_ENGINE_TRANSACTION_ROLLBACK = 1622
SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE = 1624
NDB_REPLICATION_SCHEMA_ERROR = 1625
CONFLICT_FN_PARSE_ERROR = 1626
EXCEPTIONS_WRITE_ERROR = 1627
TOO_LONG_TABLE_COMMENT = 1628
TOO_LONG_FIELD_COMMENT = 1629
FUNC_INEXISTENT_NAME_COLLISION = 1630
DATABASE_NAME = 1631
TABLE_NAME = 1632
PARTITION_NAME = 1633
SUBPARTITION_NAME = 1634
TEMPORARY_NAME = 1635
RENAMED_NAME = 1636
TOO_MANY_CONCURRENT_TRXS = 1637
WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED = 1638
DEBUG_SYNC_TIMEOUT = 1639
DEBUG_SYNC_HIT_LIMIT = 1640
DUP_SIGNAL_SET = 1641
SIGNAL_WARN = 1642
SIGNAL_NOT_FOUND = 1643
SIGNAL_EXCEPTION = 1644
RESIGNAL_WITHOUT_ACTIVE_HANDLER = 1645
SIGNAL_BAD_CONDITION_TYPE = 1646
WARN_COND_ITEM_TRUNCATED = 1647
COND_ITEM_TOO_LONG = 1648
UNKNOWN_LOCALE = 1649
SLAVE_IGNORE_SERVER_IDS = 1650
SAME_NAME_PARTITION_FIELD = 1652
PARTITION_COLUMN_LIST_ERROR = 1653
WRONG_TYPE_COLUMN_VALUE_ERROR = 1654
TOO_MANY_PARTITION_FUNC_FIELDS_ERROR = 1655
MAXVALUE_IN_VALUES_IN = 1656
TOO_MANY_VALUES_ERROR = 1657
ROW_SINGLE_PARTITION_FIELD_ERROR = 1658
FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD = 1659
PARTITION_FIELDS_TOO_LONG = 1660
BINLOG_ROW_ENGINE_AND_STMT_ENGINE = 1661
BINLOG_ROW_MODE_AND_STMT_ENGINE = 1662
BINLOG_UNSAFE_AND_STMT_ENGINE = 1663
BINLOG_ROW_INJECTION_AND_STMT_ENGINE = 1664
BINLOG_STMT_MODE_AND_ROW_ENGINE = 1665
BINLOG_ROW_INJECTION_AND_STMT_MODE = 1666
BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1667
BINLOG_UNSAFE_LIMIT = 1668
BINLOG_UNSAFE_SYSTEM_TABLE = 1670
BINLOG_UNSAFE_AUTOINC_COLUMNS = 1671
BINLOG_UNSAFE_UDF = 1672
BINLOG_UNSAFE_SYSTEM_VARIABLE = 1673
BINLOG_UNSAFE_SYSTEM_FUNCTION = 1674
BINLOG_UNSAFE_NONTRANS_AFTER_TRANS = 1675
MESSAGE_AND_STATEMENT = 1676
SLAVE_CANT_CREATE_CONVERSION = 1678
INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1679
PATH_LENGTH = 1680
WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT = 1681
WRONG_NATIVE_TABLE_STRUCTURE = 1682
WRONG_PERFSCHEMA_USAGE = 1683
WARN_I_S_SKIPPED_TABLE = 1684
INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1685
STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1686
SPATIAL_MUST_HAVE_GEOM_COL = 1687
TOO_LONG_INDEX_COMMENT = 1688
LOCK_ABORTED = 1689
DATA_OUT_OF_RANGE = 1690
WRONG_SPVAR_TYPE_IN_LIMIT = 1691
BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1692
BINLOG_UNSAFE_MIXED_STATEMENT = 1693
INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1694
STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1695
FAILED_READ_FROM_PAR_FILE = 1696
VALUES_IS_NOT_INT_TYPE_ERROR = 1697
ACCESS_DENIED_NO_PASSWORD_ERROR = 1698
SET_PASSWORD_AUTH_PLUGIN = 1699
TRUNCATE_ILLEGAL_FK = 1701
PLUGIN_IS_PERMANENT = 1702
SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN = 1703
SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX = 1704
STMT_CACHE_FULL = 1705
MULTI_UPDATE_KEY_CONFLICT = 1706
TABLE_NEEDS_REBUILD = 1707
WARN_OPTION_BELOW_LIMIT = 1708
INDEX_COLUMN_TOO_LONG = 1709
ERROR_IN_TRIGGER_BODY = 1710
ERROR_IN_UNKNOWN_TRIGGER_BODY = 1711
INDEX_CORRUPT = 1712
UNDO_RECORD_TOO_BIG = 1713
BINLOG_UNSAFE_INSERT_IGNORE_SELECT = 1714
BINLOG_UNSAFE_INSERT_SELECT_UPDATE = 1715
BINLOG_UNSAFE_REPLACE_SELECT = 1716
BINLOG_UNSAFE_CREATE_IGNORE_SELECT = 1717
BINLOG_UNSAFE_CREATE_REPLACE_SELECT = 1718
BINLOG_UNSAFE_UPDATE_IGNORE = 1719
PLUGIN_NO_UNINSTALL = 1720
PLUGIN_NO_INSTALL = 1721
BINLOG_UNSAFE_WRITE_AUTOINC_SELECT = 1722
BINLOG_UNSAFE_CREATE_SELECT_AUTOINC = 1723
BINLOG_UNSAFE_INSERT_TWO_KEYS = 1724
TABLE_IN_FK_CHECK = 1725
UNSUPPORTED_ENGINE = 1726
BINLOG_UNSAFE_AUTOINC_NOT_FIRST = 1727
CANNOT_LOAD_FROM_TABLE_V2 = 1728
MASTER_DELAY_VALUE_OUT_OF_RANGE = 1729
ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT = 1730
PARTITION_EXCHANGE_DIFFERENT_OPTION = 1731
PARTITION_EXCHANGE_PART_TABLE = 1732
PARTITION_EXCHANGE_TEMP_TABLE = 1733
PARTITION_INSTEAD_OF_SUBPARTITION = 1734
UNKNOWN_PARTITION = 1735
TABLES_DIFFERENT_METADATA = 1736
ROW_DOES_NOT_MATCH_PARTITION = 1737
BINLOG_CACHE_SIZE_GREATER_THAN_MAX = 1738
WARN_INDEX_NOT_APPLICABLE = 1739
PARTITION_EXCHANGE_FOREIGN_KEY = 1740
RPL_INFO_DATA_TOO_LONG = 1742
BINLOG_STMT_CACHE_SIZE_GREATER_THAN_MAX = 1745
CANT_UPDATE_TABLE_IN_CREATE_TABLE_SELECT = 1746
PARTITION_CLAUSE_ON_NONPARTITIONED = 1747
ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET = 1748
CHANGE_RPL_INFO_REPOSITORY_FAILURE = 1750
WARNING_NOT_COMPLETE_ROLLBACK_WITH_CREATED_TEMP_TABLE = 1751
WARNING_NOT_COMPLETE_ROLLBACK_WITH_DROPPED_TEMP_TABLE = 1752
MTS_FEATURE_IS_NOT_SUPPORTED = 1753
MTS_UPDATED_DBS_GREATER_MAX = 1754
MTS_CANT_PARALLEL = 1755
MTS_INCONSISTENT_DATA = 1756
FULLTEXT_NOT_SUPPORTED_WITH_PARTITIONING = 1757
DA_INVALID_CONDITION_NUMBER = 1758
INSECURE_PLAIN_TEXT = 1759
INSECURE_CHANGE_MASTER = 1760
FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO = 1761
FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO = 1762
SQLTHREAD_WITH_SECURE_SLAVE = 1763
TABLE_HAS_NO_FT = 1764
VARIABLE_NOT_SETTABLE_IN_SF_OR_TRIGGER = 1765
VARIABLE_NOT_SETTABLE_IN_TRANSACTION = 1766
SET_STATEMENT_CANNOT_INVOKE_FUNCTION = 1769
GTID_NEXT_CANT_BE_AUTOMATIC_IF_GTID_NEXT_LIST_IS_NON_NULL = 1770
MALFORMED_GTID_SET_SPECIFICATION = 1772
MALFORMED_GTID_SET_ENCODING = 1773
MALFORMED_GTID_SPECIFICATION = 1774
GNO_EXHAUSTED = 1775
BAD_SLAVE_AUTO_POSITION = 1776
AUTO_POSITION_REQUIRES_GTID_MODE_NOT_OFF = 1777
CANT_DO_IMPLICIT_COMMIT_IN_TRX_WHEN_GTID_NEXT_IS_SET = 1778
GTID_MODE_ON_REQUIRES_ENFORCE_GTID_CONSISTENCY_ON = 1779
CANT_SET_GTID_NEXT_TO_GTID_WHEN_GTID_MODE_IS_OFF = 1781
CANT_SET_GTID_NEXT_TO_ANONYMOUS_WHEN_GTID_MODE_IS_ON = 1782
CANT_SET_GTID_NEXT_LIST_TO_NON_NULL_WHEN_GTID_MODE_IS_OFF = 1783
GTID_UNSAFE_NON_TRANSACTIONAL_TABLE = 1785
GTID_UNSAFE_CREATE_SELECT = 1786
GTID_UNSAFE_CREATE_DROP_TEMPORARY_TABLE_IN_TRANSACTION = 1787
GTID_MODE_CAN_ONLY_CHANGE_ONE_STEP_AT_A_TIME = 1788
MASTER_HAS_PURGED_REQUIRED_GTIDS = 1789
CANT_SET_GTID_NEXT_WHEN_OWNING_GTID = 1790
UNKNOWN_EXPLAIN_FORMAT = 1791
CANT_EXECUTE_IN_READ_ONLY_TRANSACTION = 1792
TOO_LONG_TABLE_PARTITION_COMMENT = 1793
SLAVE_CONFIGURATION = 1794
INNODB_FT_LIMIT = 1795
INNODB_NO_FT_TEMP_TABLE = 1796
INNODB_FT_WRONG_DOCID_COLUMN = 1797
INNODB_FT_WRONG_DOCID_INDEX = 1798
INNODB_ONLINE_LOG_TOO_BIG = 1799
UNKNOWN_ALTER_ALGORITHM = 1800
UNKNOWN_ALTER_LOCK = 1801
MTS_CHANGE_MASTER_CANT_RUN_WITH_GAPS = 1802
MTS_RECOVERY_FAILURE = 1803
MTS_RESET_WORKERS = 1804
COL_COUNT_DOESNT_MATCH_CORRUPTED_V2 = 1805
SLAVE_SILENT_RETRY_TRANSACTION = 1806
DISCARD_FK_CHECKS_RUNNING = 1807
TABLE_SCHEMA_MISMATCH = 1808
TABLE_IN_SYSTEM_TABLESPACE = 1809
IO_READ_ERROR = 1810
IO_WRITE_ERROR = 1811
TABLESPACE_MISSING = 1812
TABLESPACE_EXISTS = 1813
TABLESPACE_DISCARDED = 1814
INTERNAL_ERROR = 1815
INNODB_IMPORT_ERROR = 1816
INNODB_INDEX_CORRUPT = 1817
INVALID_YEAR_COLUMN_LENGTH = 1818
NOT_VALID_PASSWORD = 1819
MUST_CHANGE_PASSWORD = 1820
FK_NO_INDEX_CHILD = 1821
FK_NO_INDEX_PARENT = 1822
FK_FAIL_ADD_SYSTEM = 1823
FK_CANNOT_OPEN_PARENT = 1824
FK_INCORRECT_OPTION = 1825
FK_DUP_NAME = 1826
PASSWORD_FORMAT = 1827
FK_COLUMN_CANNOT_DROP = 1828
FK_COLUMN_CANNOT_DROP_CHILD = 1829
FK_COLUMN_NOT_NULL = 1830
DUP_INDEX = 1831
FK_COLUMN_CANNOT_CHANGE = 1832
FK_COLUMN_CANNOT_CHANGE_CHILD = 1833
MALFORMED_PACKET = 1835
READ_ONLY_MODE = 1836
GTID_NEXT_TYPE_UNDEFINED_GTID = 1837
VARIABLE_NOT_SETTABLE_IN_SP = 1838
CANT_SET_GTID_PURGED_WHEN_GTID_EXECUTED_IS_NOT_EMPTY = 1840
CANT_SET_GTID_PURGED_WHEN_OWNED_GTIDS_IS_NOT_EMPTY = 1841
GTID_PURGED_WAS_CHANGED = 1842
GTID_EXECUTED_WAS_CHANGED = 1843
BINLOG_STMT_MODE_AND_NO_REPL_TABLES = 1844
ALTER_OPERATION_NOT_SUPPORTED = 1845
ALTER_OPERATION_NOT_SUPPORTED_REASON = 1846
ALTER_OPERATION_NOT_SUPPORTED_REASON_COPY = 1847
ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION = 1848
ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME = 1849
ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE = 1850
ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK = 1851
ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK = 1853
ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC = 1854
ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS = 1855
ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS = 1856
ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS = 1857
SQL_SLAVE_SKIP_COUNTER_NOT_SETTABLE_IN_GTID_MODE = 1858
DUP_UNKNOWN_IN_INDEX = 1859
IDENT_CAUSES_TOO_LONG_PATH = 1860
ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL = 1861
MUST_CHANGE_PASSWORD_LOGIN = 1862
ROW_IN_WRONG_PARTITION = 1863
MTS_EVENT_BIGGER_PENDING_JOBS_SIZE_MAX = 1864
BINLOG_LOGICAL_CORRUPTION = 1866
WARN_PURGE_LOG_IN_USE = 1867
WARN_PURGE_LOG_IS_ACTIVE = 1868
AUTO_INCREMENT_CONFLICT = 1869
WARN_ON_BLOCKHOLE_IN_RBR = 1870
SLAVE_MI_INIT_REPOSITORY = 1871
SLAVE_RLI_INIT_REPOSITORY = 1872
ACCESS_DENIED_CHANGE_USER_ERROR = 1873
INNODB_READ_ONLY = 1874
STOP_SLAVE_SQL_THREAD_TIMEOUT = 1875
STOP_SLAVE_IO_THREAD_TIMEOUT = 1876
TABLE_CORRUPT = 1877
TEMP_FILE_WRITE_FAILURE = 1878
INNODB_FT_AUX_NOT_HEX_ID = 1879
OLD_TEMPORALS_UPGRADED = 1880
INNODB_FORCED_RECOVERY = 1881
AES_INVALID_IV = 1882
PLUGIN_CANNOT_BE_UNINSTALLED = 1883
GTID_UNSAFE_BINLOG_SPLITTABLE_STATEMENT_AND_ASSIGNED_GTID = 1884
SLAVE_HAS_MORE_GTIDS_THAN_MASTER = 1885
MISSING_KEY = 1886
ERROR_LAST = 1973

View File

@@ -1,40 +0,0 @@
"""MySQL FIELD_TYPE Constants
These constants represent the various column (field) types that are
supported by MySQL.
"""
DECIMAL = 0
TINY = 1
SHORT = 2
LONG = 3
FLOAT = 4
DOUBLE = 5
NULL = 6
TIMESTAMP = 7
LONGLONG = 8
INT24 = 9
DATE = 10
TIME = 11
DATETIME = 12
YEAR = 13
# NEWDATE = 14 # Internal to MySQL.
VARCHAR = 15
BIT = 16
# TIMESTAMP2 = 17
# DATETIME2 = 18
# TIME2 = 19
JSON = 245
NEWDECIMAL = 246
ENUM = 247
SET = 248
TINY_BLOB = 249
MEDIUM_BLOB = 250
LONG_BLOB = 251
BLOB = 252
VAR_STRING = 253
STRING = 254
GEOMETRY = 255
CHAR = TINY
INTERVAL = ENUM

View File

@@ -1,23 +0,0 @@
"""MySQL FLAG Constants
These flags are used along with the FIELD_TYPE to indicate various
properties of columns in a result set.
"""
NOT_NULL = 1
PRI_KEY = 2
UNIQUE_KEY = 4
MULTIPLE_KEY = 8
BLOB = 16
UNSIGNED = 32
ZEROFILL = 64
BINARY = 128
ENUM = 256
AUTO_INCREMENT = 512
TIMESTAMP = 1024
SET = 2048
NUM = 32768
PART_KEY = 16384
GROUP = 32768
UNIQUE = 65536

View File

@@ -1 +0,0 @@
__all__ = ["CR", "FIELD_TYPE", "CLIENT", "ER", "FLAG"]

View File

@@ -1,139 +0,0 @@
"""MySQLdb type conversion module
This module handles all the type conversions for MySQL. If the default
type conversions aren't what you need, you can make your own. The
dictionary conversions maps some kind of type to a conversion function
which returns the corresponding value:
Key: FIELD_TYPE.* (from MySQLdb.constants)
Conversion function:
Arguments: string
Returns: Python object
Key: Python type object (from types) or class
Conversion function:
Arguments: Python object of indicated type or class AND
conversion dictionary
Returns: SQL literal value
Notes: Most conversion functions can ignore the dictionary, but
it is a required parameter. It is necessary for converting
things like sequences and instances.
Don't modify conversions if you can avoid it. Instead, make copies
(with the copy() method), modify the copies, and then pass them to
MySQL.connect().
"""
from decimal import Decimal
from MySQLdb._mysql import string_literal
from MySQLdb.constants import FIELD_TYPE, FLAG
from MySQLdb.times import (
Date,
DateTimeType,
DateTime2literal,
DateTimeDeltaType,
DateTimeDelta2literal,
DateTime_or_None,
TimeDelta_or_None,
Date_or_None,
)
from MySQLdb._exceptions import ProgrammingError
import array
NoneType = type(None)
try:
ArrayType = array.ArrayType
except AttributeError:
ArrayType = array.array
def Bool2Str(s, d):
return b"1" if s else b"0"
def Set2Str(s, d):
# Only support ascii string. Not tested.
return string_literal(",".join(s))
def Thing2Str(s, d):
"""Convert something into a string via str()."""
return str(s)
def Float2Str(o, d):
s = repr(o)
if s in ("inf", "nan"):
raise ProgrammingError("%s can not be used with MySQL" % s)
if "e" not in s:
s += "e0"
return s
def None2NULL(o, d):
"""Convert None to NULL."""
return b"NULL"
def Thing2Literal(o, d):
"""Convert something into a SQL string literal. If using
MySQL-3.23 or newer, string_literal() is a method of the
_mysql.MYSQL object, and this function will be overridden with
that method when the connection is created."""
return string_literal(o)
def Decimal2Literal(o, d):
return format(o, "f")
def array2Str(o, d):
return Thing2Literal(o.tostring(), d)
# bytes or str regarding to BINARY_FLAG.
_bytes_or_str = ((FLAG.BINARY, bytes), (None, str))
conversions = {
int: Thing2Str,
float: Float2Str,
NoneType: None2NULL,
ArrayType: array2Str,
bool: Bool2Str,
Date: Thing2Literal,
DateTimeType: DateTime2literal,
DateTimeDeltaType: DateTimeDelta2literal,
set: Set2Str,
Decimal: Decimal2Literal,
FIELD_TYPE.TINY: int,
FIELD_TYPE.SHORT: int,
FIELD_TYPE.LONG: int,
FIELD_TYPE.FLOAT: float,
FIELD_TYPE.DOUBLE: float,
FIELD_TYPE.DECIMAL: Decimal,
FIELD_TYPE.NEWDECIMAL: Decimal,
FIELD_TYPE.LONGLONG: int,
FIELD_TYPE.INT24: int,
FIELD_TYPE.YEAR: int,
FIELD_TYPE.TIMESTAMP: DateTime_or_None,
FIELD_TYPE.DATETIME: DateTime_or_None,
FIELD_TYPE.TIME: TimeDelta_or_None,
FIELD_TYPE.DATE: Date_or_None,
FIELD_TYPE.TINY_BLOB: bytes,
FIELD_TYPE.MEDIUM_BLOB: bytes,
FIELD_TYPE.LONG_BLOB: bytes,
FIELD_TYPE.BLOB: bytes,
FIELD_TYPE.STRING: bytes,
FIELD_TYPE.VAR_STRING: bytes,
FIELD_TYPE.VARCHAR: bytes,
FIELD_TYPE.JSON: bytes,
}

View File

@@ -1,489 +0,0 @@
"""MySQLdb Cursors
This module implements Cursors of various types for MySQLdb. By
default, MySQLdb uses the Cursor class.
"""
import re
from ._exceptions import ProgrammingError
#: Regular expression for :meth:`Cursor.executemany`.
#: executemany only supports simple bulk insert.
#: You can use it to load large dataset.
RE_INSERT_VALUES = re.compile(
"".join(
[
r"\s*((?:INSERT|REPLACE)\b.+\bVALUES?\s*)",
r"(\(\s*(?:%s|%\(.+\)s)\s*(?:,\s*(?:%s|%\(.+\)s)\s*)*\))",
r"(\s*(?:ON DUPLICATE.*)?);?\s*\Z",
]
),
re.IGNORECASE | re.DOTALL,
)
class BaseCursor:
"""A base for Cursor classes. Useful attributes:
description
A tuple of DB API 7-tuples describing the columns in
the last executed query; see PEP-249 for details.
description_flags
Tuple of column flags for last query, one entry per column
in the result set. Values correspond to those in
MySQLdb.constants.FLAG. See MySQL documentation (C API)
for more information. Non-standard extension.
arraysize
default number of rows fetchmany() will fetch
"""
#: Max statement size which :meth:`executemany` generates.
#:
#: Max size of allowed statement is max_allowed_packet - packet_header_size.
#: Default value of max_allowed_packet is 1048576.
max_stmt_length = 64 * 1024
from ._exceptions import (
MySQLError,
Warning,
Error,
InterfaceError,
DatabaseError,
DataError,
OperationalError,
IntegrityError,
InternalError,
ProgrammingError,
NotSupportedError,
)
connection = None
def __init__(self, connection):
self.connection = connection
self.description = None
self.description_flags = None
self.rowcount = -1
self.arraysize = 1
self._executed = None
self.lastrowid = None
self._result = None
self.rownumber = None
self._rows = None
def close(self):
"""Close the cursor. No further queries will be possible."""
try:
if self.connection is None:
return
while self.nextset():
pass
finally:
self.connection = None
self._result = None
def __enter__(self):
return self
def __exit__(self, *exc_info):
del exc_info
self.close()
def _escape_args(self, args, conn):
encoding = conn.encoding
literal = conn.literal
def ensure_bytes(x):
if isinstance(x, str):
return x.encode(encoding)
elif isinstance(x, tuple):
return tuple(map(ensure_bytes, x))
elif isinstance(x, list):
return list(map(ensure_bytes, x))
return x
if isinstance(args, (tuple, list)):
ret = tuple(literal(ensure_bytes(arg)) for arg in args)
elif isinstance(args, dict):
ret = {
ensure_bytes(key): literal(ensure_bytes(val))
for (key, val) in args.items()
}
else:
# If it's not a dictionary let's try escaping it anyways.
# Worst case it will throw a Value error
ret = literal(ensure_bytes(args))
ensure_bytes = None # break circular reference
return ret
def _check_executed(self):
if not self._executed:
raise ProgrammingError("execute() first")
def nextset(self):
"""Advance to the next result set.
Returns None if there are no more result sets.
"""
if self._executed:
self.fetchall()
db = self._get_db()
nr = db.next_result()
if nr == -1:
return None
self._do_get_result(db)
self._post_get_result()
return 1
def _do_get_result(self, db):
self._result = result = self._get_result()
if result is None:
self.description = self.description_flags = None
else:
self.description = result.describe()
self.description_flags = result.field_flags()
self.rowcount = db.affected_rows()
self.rownumber = 0
self.lastrowid = db.insert_id()
def _post_get_result(self):
pass
def setinputsizes(self, *args):
"""Does nothing, required by DB API."""
def setoutputsizes(self, *args):
"""Does nothing, required by DB API."""
def _get_db(self):
con = self.connection
if con is None:
raise ProgrammingError("cursor closed")
return con
def execute(self, query, args=None):
"""Execute a query.
query -- string, query to execute on server
args -- optional sequence or mapping, parameters to use with query.
Note: If args is a sequence, then %s must be used as the
parameter placeholder in the query. If a mapping is used,
%(key)s must be used as the placeholder.
Returns integer represents rows affected, if any
"""
while self.nextset():
pass
db = self._get_db()
if isinstance(query, str):
query = query.encode(db.encoding)
if args is not None:
if isinstance(args, dict):
nargs = {}
for key, item in args.items():
if isinstance(key, str):
key = key.encode(db.encoding)
nargs[key] = db.literal(item)
args = nargs
else:
args = tuple(map(db.literal, args))
try:
query = query % args
except TypeError as m:
raise ProgrammingError(str(m))
assert isinstance(query, (bytes, bytearray))
res = self._query(query)
return res
def executemany(self, query, args):
# type: (str, list) -> int
"""Execute a multi-row query.
:param query: query to execute on server
:param args: Sequence of sequences or mappings. It is used as parameter.
:return: Number of rows affected, if any.
This method improves performance on multiple-row INSERT and
REPLACE. Otherwise it is equivalent to looping over args with
execute().
"""
if not args:
return
m = RE_INSERT_VALUES.match(query)
if m:
q_prefix = m.group(1) % ()
q_values = m.group(2).rstrip()
q_postfix = m.group(3) or ""
assert q_values[0] == "(" and q_values[-1] == ")"
return self._do_execute_many(
q_prefix,
q_values,
q_postfix,
args,
self.max_stmt_length,
self._get_db().encoding,
)
self.rowcount = sum(self.execute(query, arg) for arg in args)
return self.rowcount
def _do_execute_many(
self, prefix, values, postfix, args, max_stmt_length, encoding
):
conn = self._get_db()
escape = self._escape_args
if isinstance(prefix, str):
prefix = prefix.encode(encoding)
if isinstance(values, str):
values = values.encode(encoding)
if isinstance(postfix, str):
postfix = postfix.encode(encoding)
sql = bytearray(prefix)
args = iter(args)
v = values % escape(next(args), conn)
sql += v
rows = 0
for arg in args:
v = values % escape(arg, conn)
if len(sql) + len(v) + len(postfix) + 1 > max_stmt_length:
rows += self.execute(sql + postfix)
sql = bytearray(prefix)
else:
sql += b","
sql += v
rows += self.execute(sql + postfix)
self.rowcount = rows
return rows
def callproc(self, procname, args=()):
"""Execute stored procedure procname with args
procname -- string, name of procedure to execute on server
args -- Sequence of parameters to use with procedure
Returns the original args.
Compatibility warning: PEP-249 specifies that any modified
parameters must be returned. This is currently impossible
as they are only available by storing them in a server
variable and then retrieved by a query. Since stored
procedures return zero or more result sets, there is no
reliable way to get at OUT or INOUT parameters via callproc.
The server variables are named @_procname_n, where procname
is the parameter above and n is the position of the parameter
(from zero). Once all result sets generated by the procedure
have been fetched, you can issue a SELECT @_procname_0, ...
query using .execute() to get any OUT or INOUT values.
Compatibility warning: The act of calling a stored procedure
itself creates an empty result set. This appears after any
result sets generated by the procedure. This is non-standard
behavior with respect to the DB-API. Be sure to use nextset()
to advance through all result sets; otherwise you may get
disconnected.
"""
db = self._get_db()
if isinstance(procname, str):
procname = procname.encode(db.encoding)
if args:
fmt = b"@_" + procname + b"_%d=%s"
q = b"SET %s" % b",".join(
fmt % (index, db.literal(arg)) for index, arg in enumerate(args)
)
self._query(q)
self.nextset()
q = b"CALL %s(%s)" % (
procname,
b",".join([b"@_%s_%d" % (procname, i) for i in range(len(args))]),
)
self._query(q)
return args
def _query(self, q):
db = self._get_db()
self._result = None
db.query(q)
self._do_get_result(db)
self._post_get_result()
self._executed = q
return self.rowcount
def _fetch_row(self, size=1):
if not self._result:
return ()
return self._result.fetch_row(size, self._fetch_type)
def __iter__(self):
return iter(self.fetchone, None)
Warning = Warning
Error = Error
InterfaceError = InterfaceError
DatabaseError = DatabaseError
DataError = DataError
OperationalError = OperationalError
IntegrityError = IntegrityError
InternalError = InternalError
ProgrammingError = ProgrammingError
NotSupportedError = NotSupportedError
class CursorStoreResultMixIn:
"""This is a MixIn class which causes the entire result set to be
stored on the client side, i.e. it uses mysql_store_result(). If the
result set can be very large, consider adding a LIMIT clause to your
query, or using CursorUseResultMixIn instead."""
def _get_result(self):
return self._get_db().store_result()
def _post_get_result(self):
self._rows = self._fetch_row(0)
self._result = None
def fetchone(self):
"""Fetches a single row from the cursor. None indicates that
no more rows are available."""
self._check_executed()
if self.rownumber >= len(self._rows):
return None
result = self._rows[self.rownumber]
self.rownumber = self.rownumber + 1
return result
def fetchmany(self, size=None):
"""Fetch up to size rows from the cursor. Result set may be smaller
than size. If size is not defined, cursor.arraysize is used."""
self._check_executed()
end = self.rownumber + (size or self.arraysize)
result = self._rows[self.rownumber : end]
self.rownumber = min(end, len(self._rows))
return result
def fetchall(self):
"""Fetches all available rows from the cursor."""
self._check_executed()
if self.rownumber:
result = self._rows[self.rownumber :]
else:
result = self._rows
self.rownumber = len(self._rows)
return result
def scroll(self, value, mode="relative"):
"""Scroll the cursor in the result set to a new position according
to mode.
If mode is 'relative' (default), value is taken as offset to
the current position in the result set, if set to 'absolute',
value states an absolute target position."""
self._check_executed()
if mode == "relative":
r = self.rownumber + value
elif mode == "absolute":
r = value
else:
raise ProgrammingError("unknown scroll mode %s" % repr(mode))
if r < 0 or r >= len(self._rows):
raise IndexError("out of range")
self.rownumber = r
def __iter__(self):
self._check_executed()
result = self.rownumber and self._rows[self.rownumber :] or self._rows
return iter(result)
class CursorUseResultMixIn:
"""This is a MixIn class which causes the result set to be stored
in the server and sent row-by-row to client side, i.e. it uses
mysql_use_result(). You MUST retrieve the entire result set and
close() the cursor before additional queries can be performed on
the connection."""
def _get_result(self):
return self._get_db().use_result()
def fetchone(self):
"""Fetches a single row from the cursor."""
self._check_executed()
r = self._fetch_row(1)
if not r:
return None
self.rownumber = self.rownumber + 1
return r[0]
def fetchmany(self, size=None):
"""Fetch up to size rows from the cursor. Result set may be smaller
than size. If size is not defined, cursor.arraysize is used."""
self._check_executed()
r = self._fetch_row(size or self.arraysize)
self.rownumber = self.rownumber + len(r)
return r
def fetchall(self):
"""Fetches all available rows from the cursor."""
self._check_executed()
r = self._fetch_row(0)
self.rownumber = self.rownumber + len(r)
return r
def __iter__(self):
return self
def next(self):
row = self.fetchone()
if row is None:
raise StopIteration
return row
__next__ = next
class CursorTupleRowsMixIn:
"""This is a MixIn class that causes all rows to be returned as tuples,
which is the standard form required by DB API."""
_fetch_type = 0
class CursorDictRowsMixIn:
"""This is a MixIn class that causes all rows to be returned as
dictionaries. This is a non-standard feature."""
_fetch_type = 1
class Cursor(CursorStoreResultMixIn, CursorTupleRowsMixIn, BaseCursor):
"""This is the standard Cursor class that returns rows as tuples
and stores the result set in the client."""
class DictCursor(CursorStoreResultMixIn, CursorDictRowsMixIn, BaseCursor):
"""This is a Cursor class that returns rows as dictionaries and
stores the result set in the client."""
class SSCursor(CursorUseResultMixIn, CursorTupleRowsMixIn, BaseCursor):
"""This is a Cursor class that returns rows as tuples and stores
the result set in the server."""
class SSDictCursor(CursorUseResultMixIn, CursorDictRowsMixIn, BaseCursor):
"""This is a Cursor class that returns rows as dictionaries and
stores the result set in the server."""

View File

@@ -1,4 +0,0 @@
__author__ = "Inada Naoki <songofacandy@gmail.com>"
version_info = (2,1,0,'final',0)
__version__ = "2.1.0"

View File

@@ -1,150 +0,0 @@
"""times module
This module provides some Date and Time classes for dealing with MySQL data.
Use Python datetime module to handle date and time columns.
"""
from time import localtime
from datetime import date, datetime, time, timedelta
from MySQLdb._mysql import string_literal
Date = date
Time = time
TimeDelta = timedelta
Timestamp = datetime
DateTimeDeltaType = timedelta
DateTimeType = datetime
def DateFromTicks(ticks):
"""Convert UNIX ticks into a date instance."""
return date(*localtime(ticks)[:3])
def TimeFromTicks(ticks):
"""Convert UNIX ticks into a time instance."""
return time(*localtime(ticks)[3:6])
def TimestampFromTicks(ticks):
"""Convert UNIX ticks into a datetime instance."""
return datetime(*localtime(ticks)[:6])
format_TIME = format_DATE = str
def format_TIMEDELTA(v):
seconds = int(v.seconds) % 60
minutes = int(v.seconds // 60) % 60
hours = int(v.seconds // 3600) % 24
return "%d %d:%d:%d" % (v.days, hours, minutes, seconds)
def format_TIMESTAMP(d):
"""
:type d: datetime.datetime
"""
if d.microsecond:
fmt = " ".join(
[
"{0.year:04}-{0.month:02}-{0.day:02}",
"{0.hour:02}:{0.minute:02}:{0.second:02}.{0.microsecond:06}",
]
)
else:
fmt = " ".join(
[
"{0.year:04}-{0.month:02}-{0.day:02}",
"{0.hour:02}:{0.minute:02}:{0.second:02}",
]
)
return fmt.format(d)
def DateTime_or_None(s):
try:
if len(s) < 11:
return Date_or_None(s)
micros = s[20:]
if len(micros) == 0:
# 12:00:00
micros = 0
elif len(micros) < 7:
# 12:00:00.123456
micros = int(micros) * 10 ** (6 - len(micros))
else:
return None
return datetime(
int(s[:4]), # year
int(s[5:7]), # month
int(s[8:10]), # day
int(s[11:13] or 0), # hour
int(s[14:16] or 0), # minute
int(s[17:19] or 0), # second
micros, # microsecond
)
except ValueError:
return None
def TimeDelta_or_None(s):
try:
h, m, s = s.split(":")
if "." in s:
s, ms = s.split(".")
ms = ms.ljust(6, "0")
else:
ms = 0
if h[0] == "-":
negative = True
else:
negative = False
h, m, s, ms = abs(int(h)), int(m), int(s), int(ms)
td = timedelta(hours=h, minutes=m, seconds=s, microseconds=ms)
if negative:
return -td
else:
return td
except ValueError:
# unpacking or int/float conversion failed
return None
def Time_or_None(s):
try:
h, m, s = s.split(":")
if "." in s:
s, ms = s.split(".")
ms = ms.ljust(6, "0")
else:
ms = 0
h, m, s, ms = int(h), int(m), int(s), int(ms)
return time(hour=h, minute=m, second=s, microsecond=ms)
except ValueError:
return None
def Date_or_None(s):
try:
return date(
int(s[:4]),
int(s[5:7]),
int(s[8:10]),
) # year # month # day
except ValueError:
return None
def DateTime2literal(d, c):
"""Format a DateTime object as an ISO timestamp."""
return string_literal(format_TIMESTAMP(d))
def DateTimeDelta2literal(d, c):
"""Format a DateTimeDelta object as a time."""
return string_literal(format_TIMEDELTA(d))

View File

@@ -1,222 +0,0 @@
# don't import any costly modules
import sys
import os
is_pypy = '__pypy__' in sys.builtin_module_names
def warn_distutils_present():
if 'distutils' not in sys.modules:
return
if is_pypy and sys.version_info < (3, 7):
# PyPy for 3.6 unconditionally imports distutils, so bypass the warning
# https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250
return
import warnings
warnings.warn(
"Distutils was imported before Setuptools, but importing Setuptools "
"also replaces the `distutils` module in `sys.modules`. This may lead "
"to undesirable behaviors or errors. To avoid these issues, avoid "
"using distutils directly, ensure that setuptools is installed in the "
"traditional way (e.g. not an editable install), and/or make sure "
"that setuptools is always imported before distutils."
)
def clear_distutils():
if 'distutils' not in sys.modules:
return
import warnings
warnings.warn("Setuptools is replacing distutils.")
mods = [
name
for name in sys.modules
if name == "distutils" or name.startswith("distutils.")
]
for name in mods:
del sys.modules[name]
def enabled():
"""
Allow selection of distutils by environment variable.
"""
which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local')
return which == 'local'
def ensure_local_distutils():
import importlib
clear_distutils()
# With the DistutilsMetaFinder in place,
# perform an import to cause distutils to be
# loaded from setuptools._distutils. Ref #2906.
with shim():
importlib.import_module('distutils')
# check that submodules load as expected
core = importlib.import_module('distutils.core')
assert '_distutils' in core.__file__, core.__file__
assert 'setuptools._distutils.log' not in sys.modules
def do_override():
"""
Ensure that the local copy of distutils is preferred over stdlib.
See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401
for more motivation.
"""
if enabled():
warn_distutils_present()
ensure_local_distutils()
class _TrivialRe:
def __init__(self, *patterns):
self._patterns = patterns
def match(self, string):
return all(pat in string for pat in self._patterns)
class DistutilsMetaFinder:
def find_spec(self, fullname, path, target=None):
# optimization: only consider top level modules and those
# found in the CPython test suite.
if path is not None and not fullname.startswith('test.'):
return
method_name = 'spec_for_{fullname}'.format(**locals())
method = getattr(self, method_name, lambda: None)
return method()
def spec_for_distutils(self):
if self.is_cpython():
return
import importlib
import importlib.abc
import importlib.util
try:
mod = importlib.import_module('setuptools._distutils')
except Exception:
# There are a couple of cases where setuptools._distutils
# may not be present:
# - An older Setuptools without a local distutils is
# taking precedence. Ref #2957.
# - Path manipulation during sitecustomize removes
# setuptools from the path but only after the hook
# has been loaded. Ref #2980.
# In either case, fall back to stdlib behavior.
return
class DistutilsLoader(importlib.abc.Loader):
def create_module(self, spec):
mod.__name__ = 'distutils'
return mod
def exec_module(self, module):
pass
return importlib.util.spec_from_loader(
'distutils', DistutilsLoader(), origin=mod.__file__
)
@staticmethod
def is_cpython():
"""
Suppress supplying distutils for CPython (build and tests).
Ref #2965 and #3007.
"""
return os.path.isfile('pybuilddir.txt')
def spec_for_pip(self):
"""
Ensure stdlib distutils when running under pip.
See pypa/pip#8761 for rationale.
"""
if self.pip_imported_during_build():
return
clear_distutils()
self.spec_for_distutils = lambda: None
@classmethod
def pip_imported_during_build(cls):
"""
Detect if pip is being imported in a build script. Ref #2355.
"""
import traceback
return any(
cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None)
)
@staticmethod
def frame_file_is_setup(frame):
"""
Return True if the indicated frame suggests a setup.py file.
"""
# some frames may not have __file__ (#2940)
return frame.f_globals.get('__file__', '').endswith('setup.py')
def spec_for_sensitive_tests(self):
"""
Ensure stdlib distutils when running select tests under CPython.
python/cpython#91169
"""
clear_distutils()
self.spec_for_distutils = lambda: None
sensitive_tests = (
[
'test.test_distutils',
'test.test_peg_generator',
'test.test_importlib',
]
if sys.version_info < (3, 10)
else [
'test.test_distutils',
]
)
for name in DistutilsMetaFinder.sensitive_tests:
setattr(
DistutilsMetaFinder,
f'spec_for_{name}',
DistutilsMetaFinder.spec_for_sensitive_tests,
)
DISTUTILS_FINDER = DistutilsMetaFinder()
def add_shim():
DISTUTILS_FINDER in sys.meta_path or insert_shim()
class shim:
def __enter__(self):
insert_shim()
def __exit__(self, exc, value, tb):
remove_shim()
def insert_shim():
sys.meta_path.insert(0, DISTUTILS_FINDER)
def remove_shim():
try:
sys.meta_path.remove(DISTUTILS_FINDER)
except ValueError:
pass

View File

@@ -1 +0,0 @@
__import__('_distutils_hack').do_override()

View File

@@ -1,27 +0,0 @@
Copyright (c) Django Software Foundation and individual contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. 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.
3. Neither the name of Django nor the names of its contributors may 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,248 +0,0 @@
Metadata-Version: 2.1
Name: asgiref
Version: 3.4.1
Summary: ASGI specs, helper code, and adapters
Home-page: https://github.com/django/asgiref/
Author: Django Software Foundation
Author-email: foundation@djangoproject.com
License: BSD
Project-URL: Documentation, https://asgi.readthedocs.io/
Project-URL: Further Documentation, https://docs.djangoproject.com/en/stable/topics/async/#async-adapter-functions
Project-URL: Changelog, https://github.com/django/asgiref/blob/master/CHANGELOG.txt
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.6
License-File: LICENSE
Requires-Dist: typing-extensions ; python_version < "3.8"
Provides-Extra: tests
Requires-Dist: pytest ; extra == 'tests'
Requires-Dist: pytest-asyncio ; extra == 'tests'
Requires-Dist: mypy (>=0.800) ; extra == 'tests'
asgiref
=======
.. image:: https://api.travis-ci.org/django/asgiref.svg
:target: https://travis-ci.org/django/asgiref
.. image:: https://img.shields.io/pypi/v/asgiref.svg
:target: https://pypi.python.org/pypi/asgiref
ASGI is a standard for Python asynchronous web apps and servers to communicate
with each other, and positioned as an asynchronous successor to WSGI. You can
read more at https://asgi.readthedocs.io/en/latest/
This package includes ASGI base libraries, such as:
* Sync-to-async and async-to-sync function wrappers, ``asgiref.sync``
* Server base classes, ``asgiref.server``
* A WSGI-to-ASGI adapter, in ``asgiref.wsgi``
Function wrappers
-----------------
These allow you to wrap or decorate async or sync functions to call them from
the other style (so you can call async functions from a synchronous thread,
or vice-versa).
In particular:
* AsyncToSync lets a synchronous subthread stop and wait while the async
function is called on the main thread's event loop, and then control is
returned to the thread when the async function is finished.
* SyncToAsync lets async code call a synchronous function, which is run in
a threadpool and control returned to the async coroutine when the synchronous
function completes.
The idea is to make it easier to call synchronous APIs from async code and
asynchronous APIs from synchronous code so it's easier to transition code from
one style to the other. In the case of Channels, we wrap the (synchronous)
Django view system with SyncToAsync to allow it to run inside the (asynchronous)
ASGI server.
Note that exactly what threads things run in is very specific, and aimed to
keep maximum compatibility with old synchronous code. See
"Synchronous code & Threads" below for a full explanation. By default,
``sync_to_async`` will run all synchronous code in the program in the same
thread for safety reasons; you can disable this for more performance with
``@sync_to_async(thread_sensitive=False)``, but make sure that your code does
not rely on anything bound to threads (like database connections) when you do.
Threadlocal replacement
-----------------------
This is a drop-in replacement for ``threading.local`` that works with both
threads and asyncio Tasks. Even better, it will proxy values through from a
task-local context to a thread-local context when you use ``sync_to_async``
to run things in a threadpool, and vice-versa for ``async_to_sync``.
If you instead want true thread- and task-safety, you can set
``thread_critical`` on the Local object to ensure this instead.
Server base classes
-------------------
Includes a ``StatelessServer`` class which provides all the hard work of
writing a stateless server (as in, does not handle direct incoming sockets
but instead consumes external streams or sockets to work out what is happening).
An example of such a server would be a chatbot server that connects out to
a central chat server and provides a "connection scope" per user chatting to
it. There's only one actual connection, but the server has to separate things
into several scopes for easier writing of the code.
You can see an example of this being used in `frequensgi <https://github.com/andrewgodwin/frequensgi>`_.
WSGI-to-ASGI adapter
--------------------
Allows you to wrap a WSGI application so it appears as a valid ASGI application.
Simply wrap it around your WSGI application like so::
asgi_application = WsgiToAsgi(wsgi_application)
The WSGI application will be run in a synchronous threadpool, and the wrapped
ASGI application will be one that accepts ``http`` class messages.
Please note that not all extended features of WSGI may be supported (such as
file handles for incoming POST bodies).
Dependencies
------------
``asgiref`` requires Python 3.6 or higher.
Contributing
------------
Please refer to the
`main Channels contributing docs <https://github.com/django/channels/blob/master/CONTRIBUTING.rst>`_.
Testing
'''''''
To run tests, make sure you have installed the ``tests`` extra with the package::
cd asgiref/
pip install -e .[tests]
pytest
Building the documentation
''''''''''''''''''''''''''
The documentation uses `Sphinx <http://www.sphinx-doc.org>`_::
cd asgiref/docs/
pip install sphinx
To build the docs, you can use the default tools::
sphinx-build -b html . _build/html # or `make html`, if you've got make set up
cd _build/html
python -m http.server
...or you can use ``sphinx-autobuild`` to run a server and rebuild/reload
your documentation changes automatically::
pip install sphinx-autobuild
sphinx-autobuild . _build/html
Releasing
'''''''''
To release, first add details to CHANGELOG.txt and update the version number in ``asgiref/__init__.py``.
Then, build and push the packages::
python -m build
twine upload dist/*
rm -r build/ dist/
Implementation Details
----------------------
Synchronous code & threads
''''''''''''''''''''''''''
The ``asgiref.sync`` module provides two wrappers that let you go between
asynchronous and synchronous code at will, while taking care of the rough edges
for you.
Unfortunately, the rough edges are numerous, and the code has to work especially
hard to keep things in the same thread as much as possible. Notably, the
restrictions we are working with are:
* All synchronous code called through ``SyncToAsync`` and marked with
``thread_sensitive`` should run in the same thread as each other (and if the
outer layer of the program is synchronous, the main thread)
* If a thread already has a running async loop, ``AsyncToSync`` can't run things
on that loop if it's blocked on synchronous code that is above you in the
call stack.
The first compromise you get to might be that ``thread_sensitive`` code should
just run in the same thread and not spawn in a sub-thread, fulfilling the first
restriction, but that immediately runs you into the second restriction.
The only real solution is to essentially have a variant of ThreadPoolExecutor
that executes any ``thread_sensitive`` code on the outermost synchronous
thread - either the main thread, or a single spawned subthread.
This means you now have two basic states:
* If the outermost layer of your program is synchronous, then all async code
run through ``AsyncToSync`` will run in a per-call event loop in arbitrary
sub-threads, while all ``thread_sensitive`` code will run in the main thread.
* If the outermost layer of your program is asynchronous, then all async code
runs on the main thread's event loop, and all ``thread_sensitive`` synchronous
code will run in a single shared sub-thread.
Crucially, this means that in both cases there is a thread which is a shared
resource that all ``thread_sensitive`` code must run on, and there is a chance
that this thread is currently blocked on its own ``AsyncToSync`` call. Thus,
``AsyncToSync`` needs to act as an executor for thread code while it's blocking.
The ``CurrentThreadExecutor`` class provides this functionality; rather than
simply waiting on a Future, you can call its ``run_until_future`` method and
it will run submitted code until that Future is done. This means that code
inside the call can then run code on your thread.
Maintenance and Security
------------------------
To report security issues, please contact security@djangoproject.com. For GPG
signatures and more security process information, see
https://docs.djangoproject.com/en/dev/internals/security/.
To report bugs or request new features, please open a new GitHub issue.
This repository is part of the Channels project. For the shepherd and maintenance team, please see the
`main Channels readme <https://github.com/django/channels/blob/master/README.rst>`_.

View File

@@ -1,29 +0,0 @@
asgiref-3.4.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
asgiref-3.4.1.dist-info/LICENSE,sha256=uEZBXRtRTpwd_xSiLeuQbXlLxUbKYSn5UKGM0JHipmk,1552
asgiref-3.4.1.dist-info/METADATA,sha256=TZrVDUz2BP8ewHAkOyGQ8izCsiaq6YHMvj_TW5W7i2E,9162
asgiref-3.4.1.dist-info/RECORD,,
asgiref-3.4.1.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
asgiref-3.4.1.dist-info/top_level.txt,sha256=bokQjCzwwERhdBiPdvYEZa4cHxT4NCeAffQNUqJ8ssg,8
asgiref/__init__.py,sha256=z3MJNttjzZJkd4Yv_Ut_X2qO_gIKi4TijrHVpefXuRM,22
asgiref/__pycache__/__init__.cpython-310.pyc,,
asgiref/__pycache__/_pep562.cpython-310.pyc,,
asgiref/__pycache__/compatibility.cpython-310.pyc,,
asgiref/__pycache__/current_thread_executor.cpython-310.pyc,,
asgiref/__pycache__/local.cpython-310.pyc,,
asgiref/__pycache__/server.cpython-310.pyc,,
asgiref/__pycache__/sync.cpython-310.pyc,,
asgiref/__pycache__/testing.cpython-310.pyc,,
asgiref/__pycache__/timeout.cpython-310.pyc,,
asgiref/__pycache__/typing.cpython-310.pyc,,
asgiref/__pycache__/wsgi.cpython-310.pyc,,
asgiref/_pep562.py,sha256=fyD3JhfLtViIGeXBtvhhbnbQ-R_8-nmwzbXHhncY6ow,2684
asgiref/compatibility.py,sha256=4Plx8PT3wlDzZeuCN2cATfaXq6rya1OuSdJ262I5S6Y,2022
asgiref/current_thread_executor.py,sha256=oeH8zv2tTmcbpxdUmOSMzbEXzeY5nJzIMFvzprE95gA,2801
asgiref/local.py,sha256=D9kRIDARSUixNbxK8HL2O8vFhRCx_fc3fFB9uv0vG-g,4892
asgiref/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
asgiref/server.py,sha256=lAxZxOxkdvxB073ZtYOAzN1JZ8aV-DOiFpVQJZ0X2FI,6018
asgiref/sync.py,sha256=LCEHMPNiuoVtKrmI0kksoHeFjoSS5zLEG-n95UNd91Q,20310
asgiref/testing.py,sha256=3byNRV7Oto_Fg8Z-fErQJ3yGf7OQlcUexbN_cDQugzQ,3119
asgiref/timeout.py,sha256=UUYuUSY30dsqBsVzVAS7z9raQ9ntZGktScJw_Y_9iSU,3889
asgiref/typing.py,sha256=-2wmtHqkhzV52rbMfipGTJmo8jUoU0i5AQECFH6y7aY,6722
asgiref/wsgi.py,sha256=-L0eo_uK_dq7EPjv1meW1BRGytURaO9NPESxnJc9CtA,6575

View File

@@ -1,5 +0,0 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.36.2)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -1 +0,0 @@
__version__ = "3.4.1"

View File

@@ -1,61 +0,0 @@
"""
Backport of PEP 562.
https://pypi.org/search/?q=pep562
Licensed under MIT
Copyright (c) 2018 Isaac Muse <isaacmuse@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
"""
import sys
from typing import Any, Callable, List, Optional
class Pep562:
"""
Backport of PEP 562 <https://pypi.org/search/?q=pep562>.
Wraps the module in a class that exposes the mechanics to override `__dir__` and `__getattr__`.
The given module will be searched for overrides of `__dir__` and `__getattr__` and use them when needed.
"""
def __init__(self, name: str) -> None:
"""Acquire `__getattr__` and `__dir__`, but only replace module for versions less than Python 3.7."""
self._module = sys.modules[name]
self._get_attr = getattr(self._module, "__getattr__", None)
self._get_dir: Optional[Callable[..., List[str]]] = getattr(
self._module, "__dir__", None
)
sys.modules[name] = self # type: ignore[assignment]
def __dir__(self) -> List[str]:
"""Return the overridden `dir` if one was provided, else apply `dir` to the module."""
return self._get_dir() if self._get_dir else dir(self._module)
def __getattr__(self, name: str) -> Any:
"""
Attempt to retrieve the attribute from the module, and if missing, use the overridden function if present.
"""
try:
return getattr(self._module, name)
except AttributeError:
if self._get_attr:
return self._get_attr(name)
raise
def pep562(module_name: str) -> None:
"""Helper function to apply PEP 562."""
if sys.version_info < (3, 7):
Pep562(module_name)

View File

@@ -1,61 +0,0 @@
import asyncio
import inspect
import sys
def is_double_callable(application):
"""
Tests to see if an application is a legacy-style (double-callable) application.
"""
# Look for a hint on the object first
if getattr(application, "_asgi_single_callable", False):
return False
if getattr(application, "_asgi_double_callable", False):
return True
# Uninstanted classes are double-callable
if inspect.isclass(application):
return True
# Instanted classes depend on their __call__
if hasattr(application, "__call__"):
# We only check to see if its __call__ is a coroutine function -
# if it's not, it still might be a coroutine function itself.
if asyncio.iscoroutinefunction(application.__call__):
return False
# Non-classes we just check directly
return not asyncio.iscoroutinefunction(application)
def double_to_single_callable(application):
"""
Transforms a double-callable ASGI application into a single-callable one.
"""
async def new_application(scope, receive, send):
instance = application(scope)
return await instance(receive, send)
return new_application
def guarantee_single_callable(application):
"""
Takes either a single- or double-callable application and always returns it
in single-callable style. Use this to add backwards compatibility for ASGI
2.0 applications to your server/test harness/etc.
"""
if is_double_callable(application):
application = double_to_single_callable(application)
return application
if sys.version_info >= (3, 7):
# these were introduced in 3.7
get_running_loop = asyncio.get_running_loop
run_future = asyncio.run
create_task = asyncio.create_task
else:
# marked as deprecated in 3.10, did not exist before 3.7
get_running_loop = asyncio.get_event_loop
run_future = asyncio.ensure_future
# does nothing, this is fine for <3.7
create_task = lambda task: task

View File

@@ -1,81 +0,0 @@
import queue
import threading
from concurrent.futures import Executor, Future
class _WorkItem:
"""
Represents an item needing to be run in the executor.
Copied from ThreadPoolExecutor (but it's private, so we're not going to rely on importing it)
"""
def __init__(self, future, fn, args, kwargs):
self.future = future
self.fn = fn
self.args = args
self.kwargs = kwargs
def run(self):
if not self.future.set_running_or_notify_cancel():
return
try:
result = self.fn(*self.args, **self.kwargs)
except BaseException as exc:
self.future.set_exception(exc)
# Break a reference cycle with the exception 'exc'
self = None
else:
self.future.set_result(result)
class CurrentThreadExecutor(Executor):
"""
An Executor that actually runs code in the thread it is instantiated in.
Passed to other threads running async code, so they can run sync code in
the thread they came from.
"""
def __init__(self):
self._work_thread = threading.current_thread()
self._work_queue = queue.Queue()
self._broken = False
def run_until_future(self, future):
"""
Runs the code in the work queue until a result is available from the future.
Should be run from the thread the executor is initialised in.
"""
# Check we're in the right thread
if threading.current_thread() != self._work_thread:
raise RuntimeError(
"You cannot run CurrentThreadExecutor from a different thread"
)
future.add_done_callback(self._work_queue.put)
# Keep getting and running work items until we get the future we're waiting for
# back via the future's done callback.
try:
while True:
# Get a work item and run it
work_item = self._work_queue.get()
if work_item is future:
return
work_item.run()
del work_item
finally:
self._broken = True
def submit(self, fn, *args, **kwargs):
# Check they're not submitting from the same thread
if threading.current_thread() == self._work_thread:
raise RuntimeError(
"You cannot submit onto CurrentThreadExecutor from its own thread"
)
# Check they're not too late or the executor errored
if self._broken:
raise RuntimeError("CurrentThreadExecutor already quit or is broken")
# Add to work queue
f = Future()
work_item = _WorkItem(f, fn, args, kwargs)
self._work_queue.put(work_item)
# Return the future
return f

View File

@@ -1,122 +0,0 @@
import random
import string
import sys
import threading
import weakref
class Local:
"""
A drop-in replacement for threading.locals that also works with asyncio
Tasks (via the current_task asyncio method), and passes locals through
sync_to_async and async_to_sync.
Specifically:
- Locals work per-coroutine on any thread not spawned using asgiref
- Locals work per-thread on any thread not spawned using asgiref
- Locals are shared with the parent coroutine when using sync_to_async
- Locals are shared with the parent thread when using async_to_sync
(and if that thread was launched using sync_to_async, with its parent
coroutine as well, with this working for indefinite levels of nesting)
Set thread_critical to True to not allow locals to pass from an async Task
to a thread it spawns. This is needed for code that truly needs
thread-safety, as opposed to things used for helpful context (e.g. sqlite
does not like being called from a different thread to the one it is from).
Thread-critical code will still be differentiated per-Task within a thread
as it is expected it does not like concurrent access.
This doesn't use contextvars as it needs to support 3.6. Once it can support
3.7 only, we can then reimplement the storage more nicely.
"""
CLEANUP_INTERVAL = 60 # seconds
def __init__(self, thread_critical: bool = False) -> None:
self._thread_critical = thread_critical
self._thread_lock = threading.RLock()
self._context_refs: "weakref.WeakSet[object]" = weakref.WeakSet()
# Random suffixes stop accidental reuse between different Locals,
# though we try to force deletion as well.
self._attr_name = "_asgiref_local_impl_{}_{}".format(
id(self),
"".join(random.choice(string.ascii_letters) for i in range(8)),
)
def _get_context_id(self):
"""
Get the ID we should use for looking up variables
"""
# Prevent a circular reference
from .sync import AsyncToSync, SyncToAsync
# First, pull the current task if we can
context_id = SyncToAsync.get_current_task()
context_is_async = True
# OK, let's try for a thread ID
if context_id is None:
context_id = threading.current_thread()
context_is_async = False
# If we're thread-critical, we stop here, as we can't share contexts.
if self._thread_critical:
return context_id
# Now, take those and see if we can resolve them through the launch maps
for i in range(sys.getrecursionlimit()):
try:
if context_is_async:
# Tasks have a source thread in AsyncToSync
context_id = AsyncToSync.launch_map[context_id]
context_is_async = False
else:
# Threads have a source task in SyncToAsync
context_id = SyncToAsync.launch_map[context_id]
context_is_async = True
except KeyError:
break
else:
# Catch infinite loops (they happen if you are screwing around
# with AsyncToSync implementations)
raise RuntimeError("Infinite launch_map loops")
return context_id
def _get_storage(self):
context_obj = self._get_context_id()
if not hasattr(context_obj, self._attr_name):
setattr(context_obj, self._attr_name, {})
self._context_refs.add(context_obj)
return getattr(context_obj, self._attr_name)
def __del__(self):
try:
for context_obj in self._context_refs:
try:
delattr(context_obj, self._attr_name)
except AttributeError:
pass
except TypeError:
# WeakSet.__iter__ can crash when interpreter is shutting down due
# to _IterationGuard being None.
pass
def __getattr__(self, key):
with self._thread_lock:
storage = self._get_storage()
if key in storage:
return storage[key]
else:
raise AttributeError(f"{self!r} object has no attribute {key!r}")
def __setattr__(self, key, value):
if key in ("_context_refs", "_thread_critical", "_thread_lock", "_attr_name"):
return super().__setattr__(key, value)
with self._thread_lock:
storage = self._get_storage()
storage[key] = value
def __delattr__(self, key):
with self._thread_lock:
storage = self._get_storage()
if key in storage:
del storage[key]
else:
raise AttributeError(f"{self!r} object has no attribute {key!r}")

View File

@@ -1,157 +0,0 @@
import asyncio
import logging
import time
import traceback
from .compatibility import get_running_loop, guarantee_single_callable, run_future
logger = logging.getLogger(__name__)
class StatelessServer:
"""
Base server class that handles basic concepts like application instance
creation/pooling, exception handling, and similar, for stateless protocols
(i.e. ones without actual incoming connections to the process)
Your code should override the handle() method, doing whatever it needs to,
and calling get_or_create_application_instance with a unique `scope_id`
and `scope` for the scope it wants to get.
If an application instance is found with the same `scope_id`, you are
given its input queue, otherwise one is made for you with the scope provided
and you are given that fresh new input queue. Either way, you should do
something like:
input_queue = self.get_or_create_application_instance(
"user-123456",
{"type": "testprotocol", "user_id": "123456", "username": "andrew"},
)
input_queue.put_nowait(message)
If you try and create an application instance and there are already
`max_application` instances, the oldest/least recently used one will be
reclaimed and shut down to make space.
Application coroutines that error will be found periodically (every 100ms
by default) and have their exceptions printed to the console. Override
application_exception() if you want to do more when this happens.
If you override run(), make sure you handle things like launching the
application checker.
"""
application_checker_interval = 0.1
def __init__(self, application, max_applications=1000):
# Parameters
self.application = application
self.max_applications = max_applications
# Initialisation
self.application_instances = {}
### Mainloop and handling
def run(self):
"""
Runs the asyncio event loop with our handler loop.
"""
event_loop = get_running_loop()
asyncio.ensure_future(self.application_checker())
try:
event_loop.run_until_complete(self.handle())
except KeyboardInterrupt:
logger.info("Exiting due to Ctrl-C/interrupt")
async def handle(self):
raise NotImplementedError("You must implement handle()")
async def application_send(self, scope, message):
"""
Receives outbound sends from applications and handles them.
"""
raise NotImplementedError("You must implement application_send()")
### Application instance management
def get_or_create_application_instance(self, scope_id, scope):
"""
Creates an application instance and returns its queue.
"""
if scope_id in self.application_instances:
self.application_instances[scope_id]["last_used"] = time.time()
return self.application_instances[scope_id]["input_queue"]
# See if we need to delete an old one
while len(self.application_instances) > self.max_applications:
self.delete_oldest_application_instance()
# Make an instance of the application
input_queue = asyncio.Queue()
application_instance = guarantee_single_callable(self.application)
# Run it, and stash the future for later checking
future = run_future(
application_instance(
scope=scope,
receive=input_queue.get,
send=lambda message: self.application_send(scope, message),
),
)
self.application_instances[scope_id] = {
"input_queue": input_queue,
"future": future,
"scope": scope,
"last_used": time.time(),
}
return input_queue
def delete_oldest_application_instance(self):
"""
Finds and deletes the oldest application instance
"""
oldest_time = min(
details["last_used"] for details in self.application_instances.values()
)
for scope_id, details in self.application_instances.items():
if details["last_used"] == oldest_time:
self.delete_application_instance(scope_id)
# Return to make sure we only delete one in case two have
# the same oldest time
return
def delete_application_instance(self, scope_id):
"""
Removes an application instance (makes sure its task is stopped,
then removes it from the current set)
"""
details = self.application_instances[scope_id]
del self.application_instances[scope_id]
if not details["future"].done():
details["future"].cancel()
async def application_checker(self):
"""
Goes through the set of current application instance Futures and cleans up
any that are done/prints exceptions for any that errored.
"""
while True:
await asyncio.sleep(self.application_checker_interval)
for scope_id, details in list(self.application_instances.items()):
if details["future"].done():
exception = details["future"].exception()
if exception:
await self.application_exception(exception, details)
try:
del self.application_instances[scope_id]
except KeyError:
# Exception handling might have already got here before us. That's fine.
pass
async def application_exception(self, exception, application_details):
"""
Called whenever an application coroutine has an exception.
"""
logging.error(
"Exception inside application: %s\n%s%s",
exception,
"".join(traceback.format_tb(exception.__traceback__)),
f" {exception}",
)

View File

@@ -1,548 +0,0 @@
import asyncio.coroutines
import functools
import inspect
import os
import sys
import threading
import warnings
import weakref
from concurrent.futures import Future, ThreadPoolExecutor
from typing import Any, Callable, Dict, Optional, overload
from .compatibility import get_running_loop
from .current_thread_executor import CurrentThreadExecutor
from .local import Local
if sys.version_info >= (3, 7):
import contextvars
else:
contextvars = None
def _restore_context(context):
# Check for changes in contextvars, and set them to the current
# context for downstream consumers
for cvar in context:
try:
if cvar.get() != context.get(cvar):
cvar.set(context.get(cvar))
except LookupError:
cvar.set(context.get(cvar))
def _iscoroutinefunction_or_partial(func: Any) -> bool:
# Python < 3.8 does not correctly determine partially wrapped
# coroutine functions are coroutine functions, hence the need for
# this to exist. Code taken from CPython.
if sys.version_info >= (3, 8):
return asyncio.iscoroutinefunction(func)
else:
while inspect.ismethod(func):
func = func.__func__
while isinstance(func, functools.partial):
func = func.func
return asyncio.iscoroutinefunction(func)
class ThreadSensitiveContext:
"""Async context manager to manage context for thread sensitive mode
This context manager controls which thread pool executor is used when in
thread sensitive mode. By default, a single thread pool executor is shared
within a process.
In Python 3.7+, the ThreadSensitiveContext() context manager may be used to
specify a thread pool per context.
In Python 3.6, usage of this context manager has no effect.
This context manager is re-entrant, so only the outer-most call to
ThreadSensitiveContext will set the context.
Usage:
>>> import time
>>> async with ThreadSensitiveContext():
... await sync_to_async(time.sleep, 1)()
"""
def __init__(self):
self.token = None
if contextvars:
async def __aenter__(self):
try:
SyncToAsync.thread_sensitive_context.get()
except LookupError:
self.token = SyncToAsync.thread_sensitive_context.set(self)
return self
async def __aexit__(self, exc, value, tb):
if not self.token:
return
executor = SyncToAsync.context_to_thread_executor.pop(self, None)
if executor:
executor.shutdown()
SyncToAsync.thread_sensitive_context.reset(self.token)
else:
async def __aenter__(self):
return self
async def __aexit__(self, exc, value, tb):
pass
class AsyncToSync:
"""
Utility class which turns an awaitable that only works on the thread with
the event loop into a synchronous callable that works in a subthread.
If the call stack contains an async loop, the code runs there.
Otherwise, the code runs in a new loop in a new thread.
Either way, this thread then pauses and waits to run any thread_sensitive
code called from further down the call stack using SyncToAsync, before
finally exiting once the async task returns.
"""
# Maps launched Tasks to the threads that launched them (for locals impl)
launch_map: "Dict[asyncio.Task[object], threading.Thread]" = {}
# Keeps track of which CurrentThreadExecutor to use. This uses an asgiref
# Local, not a threadlocal, so that tasks can work out what their parent used.
executors = Local()
def __init__(self, awaitable, force_new_loop=False):
if not callable(awaitable) or not _iscoroutinefunction_or_partial(awaitable):
# Python does not have very reliable detection of async functions
# (lots of false negatives) so this is just a warning.
warnings.warn("async_to_sync was passed a non-async-marked callable")
self.awaitable = awaitable
try:
self.__self__ = self.awaitable.__self__
except AttributeError:
pass
if force_new_loop:
# They have asked that we always run in a new sub-loop.
self.main_event_loop = None
else:
try:
self.main_event_loop = get_running_loop()
except RuntimeError:
# There's no event loop in this thread. Look for the threadlocal if
# we're inside SyncToAsync
main_event_loop_pid = getattr(
SyncToAsync.threadlocal, "main_event_loop_pid", None
)
# We make sure the parent loop is from the same process - if
# they've forked, this is not going to be valid any more (#194)
if main_event_loop_pid and main_event_loop_pid == os.getpid():
self.main_event_loop = getattr(
SyncToAsync.threadlocal, "main_event_loop", None
)
else:
self.main_event_loop = None
def __call__(self, *args, **kwargs):
# You can't call AsyncToSync from a thread with a running event loop
try:
event_loop = get_running_loop()
except RuntimeError:
pass
else:
if event_loop.is_running():
raise RuntimeError(
"You cannot use AsyncToSync in the same thread as an async event loop - "
"just await the async function directly."
)
if contextvars is not None:
# Wrapping context in list so it can be reassigned from within
# `main_wrap`.
context = [contextvars.copy_context()]
else:
context = None
# Make a future for the return information
call_result = Future()
# Get the source thread
source_thread = threading.current_thread()
# Make a CurrentThreadExecutor we'll use to idle in this thread - we
# need one for every sync frame, even if there's one above us in the
# same thread.
if hasattr(self.executors, "current"):
old_current_executor = self.executors.current
else:
old_current_executor = None
current_executor = CurrentThreadExecutor()
self.executors.current = current_executor
# Use call_soon_threadsafe to schedule a synchronous callback on the
# main event loop's thread if it's there, otherwise make a new loop
# in this thread.
try:
awaitable = self.main_wrap(
args, kwargs, call_result, source_thread, sys.exc_info(), context
)
if not (self.main_event_loop and self.main_event_loop.is_running()):
# Make our own event loop - in a new thread - and run inside that.
loop = asyncio.new_event_loop()
loop_executor = ThreadPoolExecutor(max_workers=1)
loop_future = loop_executor.submit(
self._run_event_loop, loop, awaitable
)
if current_executor:
# Run the CurrentThreadExecutor until the future is done
current_executor.run_until_future(loop_future)
# Wait for future and/or allow for exception propagation
loop_future.result()
else:
# Call it inside the existing loop
self.main_event_loop.call_soon_threadsafe(
self.main_event_loop.create_task, awaitable
)
if current_executor:
# Run the CurrentThreadExecutor until the future is done
current_executor.run_until_future(call_result)
finally:
# Clean up any executor we were running
if hasattr(self.executors, "current"):
del self.executors.current
if old_current_executor:
self.executors.current = old_current_executor
if contextvars is not None:
_restore_context(context[0])
# Wait for results from the future.
return call_result.result()
def _run_event_loop(self, loop, coro):
"""
Runs the given event loop (designed to be called in a thread).
"""
asyncio.set_event_loop(loop)
try:
loop.run_until_complete(coro)
finally:
try:
# mimic asyncio.run() behavior
# cancel unexhausted async generators
if sys.version_info >= (3, 7, 0):
tasks = asyncio.all_tasks(loop)
else:
tasks = asyncio.Task.all_tasks(loop)
for task in tasks:
task.cancel()
async def gather():
await asyncio.gather(*tasks, return_exceptions=True)
loop.run_until_complete(gather())
for task in tasks:
if task.cancelled():
continue
if task.exception() is not None:
loop.call_exception_handler(
{
"message": "unhandled exception during loop shutdown",
"exception": task.exception(),
"task": task,
}
)
if hasattr(loop, "shutdown_asyncgens"):
loop.run_until_complete(loop.shutdown_asyncgens())
finally:
loop.close()
asyncio.set_event_loop(self.main_event_loop)
def __get__(self, parent, objtype):
"""
Include self for methods
"""
func = functools.partial(self.__call__, parent)
return functools.update_wrapper(func, self.awaitable)
async def main_wrap(
self, args, kwargs, call_result, source_thread, exc_info, context
):
"""
Wraps the awaitable with something that puts the result into the
result/exception future.
"""
if context is not None:
_restore_context(context[0])
current_task = SyncToAsync.get_current_task()
self.launch_map[current_task] = source_thread
try:
# If we have an exception, run the function inside the except block
# after raising it so exc_info is correctly populated.
if exc_info[1]:
try:
raise exc_info[1]
except BaseException:
result = await self.awaitable(*args, **kwargs)
else:
result = await self.awaitable(*args, **kwargs)
except BaseException as e:
call_result.set_exception(e)
else:
call_result.set_result(result)
finally:
del self.launch_map[current_task]
if context is not None:
context[0] = contextvars.copy_context()
class SyncToAsync:
"""
Utility class which turns a synchronous callable into an awaitable that
runs in a threadpool. It also sets a threadlocal inside the thread so
calls to AsyncToSync can escape it.
If thread_sensitive is passed, the code will run in the same thread as any
outer code. This is needed for underlying Python code that is not
threadsafe (for example, code which handles SQLite database connections).
If the outermost program is async (i.e. SyncToAsync is outermost), then
this will be a dedicated single sub-thread that all sync code runs in,
one after the other. If the outermost program is sync (i.e. AsyncToSync is
outermost), this will just be the main thread. This is achieved by idling
with a CurrentThreadExecutor while AsyncToSync is blocking its sync parent,
rather than just blocking.
If executor is passed in, that will be used instead of the loop's default executor.
In order to pass in an executor, thread_sensitive must be set to False, otherwise
a TypeError will be raised.
"""
# If they've set ASGI_THREADS, update the default asyncio executor for now
if "ASGI_THREADS" in os.environ:
loop = get_running_loop()
loop.set_default_executor(
ThreadPoolExecutor(max_workers=int(os.environ["ASGI_THREADS"]))
)
# Maps launched threads to the coroutines that spawned them
launch_map: "Dict[threading.Thread, asyncio.Task[object]]" = {}
# Storage for main event loop references
threadlocal = threading.local()
# Single-thread executor for thread-sensitive code
single_thread_executor = ThreadPoolExecutor(max_workers=1)
# Maintain a contextvar for the current execution context. Optionally used
# for thread sensitive mode.
if sys.version_info >= (3, 7):
thread_sensitive_context: "contextvars.ContextVar[str]" = (
contextvars.ContextVar("thread_sensitive_context")
)
else:
thread_sensitive_context: None = None
# Contextvar that is used to detect if the single thread executor
# would be awaited on while already being used in the same context
if sys.version_info >= (3, 7):
deadlock_context: "contextvars.ContextVar[bool]" = contextvars.ContextVar(
"deadlock_context"
)
else:
deadlock_context: None = None
# Maintaining a weak reference to the context ensures that thread pools are
# erased once the context goes out of scope. This terminates the thread pool.
context_to_thread_executor: "weakref.WeakKeyDictionary[object, ThreadPoolExecutor]" = (
weakref.WeakKeyDictionary()
)
def __init__(
self,
func: Callable[..., Any],
thread_sensitive: bool = True,
executor: Optional["ThreadPoolExecutor"] = None,
) -> None:
if not callable(func) or _iscoroutinefunction_or_partial(func):
raise TypeError("sync_to_async can only be applied to sync functions.")
self.func = func
functools.update_wrapper(self, func)
self._thread_sensitive = thread_sensitive
self._is_coroutine = asyncio.coroutines._is_coroutine # type: ignore
if thread_sensitive and executor is not None:
raise TypeError("executor must not be set when thread_sensitive is True")
self._executor = executor
try:
self.__self__ = func.__self__ # type: ignore
except AttributeError:
pass
async def __call__(self, *args, **kwargs):
loop = get_running_loop()
# Work out what thread to run the code in
if self._thread_sensitive:
if hasattr(AsyncToSync.executors, "current"):
# If we have a parent sync thread above somewhere, use that
executor = AsyncToSync.executors.current
elif self.thread_sensitive_context and self.thread_sensitive_context.get(
None
):
# If we have a way of retrieving the current context, attempt
# to use a per-context thread pool executor
thread_sensitive_context = self.thread_sensitive_context.get()
if thread_sensitive_context in self.context_to_thread_executor:
# Re-use thread executor in current context
executor = self.context_to_thread_executor[thread_sensitive_context]
else:
# Create new thread executor in current context
executor = ThreadPoolExecutor(max_workers=1)
self.context_to_thread_executor[thread_sensitive_context] = executor
elif self.deadlock_context and self.deadlock_context.get(False):
raise RuntimeError(
"Single thread executor already being used, would deadlock"
)
else:
# Otherwise, we run it in a fixed single thread
executor = self.single_thread_executor
if self.deadlock_context:
self.deadlock_context.set(True)
else:
# Use the passed in executor, or the loop's default if it is None
executor = self._executor
if contextvars is not None:
context = contextvars.copy_context()
child = functools.partial(self.func, *args, **kwargs)
func = context.run
args = (child,)
kwargs = {}
else:
func = self.func
try:
# Run the code in the right thread
future = loop.run_in_executor(
executor,
functools.partial(
self.thread_handler,
loop,
self.get_current_task(),
sys.exc_info(),
func,
*args,
**kwargs,
),
)
ret = await asyncio.wait_for(future, timeout=None)
finally:
if contextvars is not None:
_restore_context(context)
if self.deadlock_context:
self.deadlock_context.set(False)
return ret
def __get__(self, parent, objtype):
"""
Include self for methods
"""
return functools.partial(self.__call__, parent)
def thread_handler(self, loop, source_task, exc_info, func, *args, **kwargs):
"""
Wraps the sync application with exception handling.
"""
# Set the threadlocal for AsyncToSync
self.threadlocal.main_event_loop = loop
self.threadlocal.main_event_loop_pid = os.getpid()
# Set the task mapping (used for the locals module)
current_thread = threading.current_thread()
if AsyncToSync.launch_map.get(source_task) == current_thread:
# Our parent task was launched from this same thread, so don't make
# a launch map entry - let it shortcut over us! (and stop infinite loops)
parent_set = False
else:
self.launch_map[current_thread] = source_task
parent_set = True
# Run the function
try:
# If we have an exception, run the function inside the except block
# after raising it so exc_info is correctly populated.
if exc_info[1]:
try:
raise exc_info[1]
except BaseException:
return func(*args, **kwargs)
else:
return func(*args, **kwargs)
finally:
# Only delete the launch_map parent if we set it, otherwise it is
# from someone else.
if parent_set:
del self.launch_map[current_thread]
@staticmethod
def get_current_task():
"""
Cross-version implementation of asyncio.current_task()
Returns None if there is no task.
"""
try:
if hasattr(asyncio, "current_task"):
# Python 3.7 and up
return asyncio.current_task()
else:
# Python 3.6
return asyncio.Task.current_task()
except RuntimeError:
return None
# Lowercase aliases (and decorator friendliness)
async_to_sync = AsyncToSync
@overload
def sync_to_async(
func: None = None,
thread_sensitive: bool = True,
executor: Optional["ThreadPoolExecutor"] = None,
) -> Callable[[Callable[..., Any]], SyncToAsync]:
...
@overload
def sync_to_async(
func: Callable[..., Any],
thread_sensitive: bool = True,
executor: Optional["ThreadPoolExecutor"] = None,
) -> SyncToAsync:
...
def sync_to_async(
func=None,
thread_sensitive=True,
executor=None,
):
if func is None:
return lambda f: SyncToAsync(
f,
thread_sensitive=thread_sensitive,
executor=executor,
)
return SyncToAsync(
func,
thread_sensitive=thread_sensitive,
executor=executor,
)

View File

@@ -1,97 +0,0 @@
import asyncio
import time
from .compatibility import guarantee_single_callable
from .timeout import timeout as async_timeout
class ApplicationCommunicator:
"""
Runs an ASGI application in a test mode, allowing sending of
messages to it and retrieval of messages it sends.
"""
def __init__(self, application, scope):
self.application = guarantee_single_callable(application)
self.scope = scope
self.input_queue = asyncio.Queue()
self.output_queue = asyncio.Queue()
self.future = asyncio.ensure_future(
self.application(scope, self.input_queue.get, self.output_queue.put)
)
async def wait(self, timeout=1):
"""
Waits for the application to stop itself and returns any exceptions.
"""
try:
async with async_timeout(timeout):
try:
await self.future
self.future.result()
except asyncio.CancelledError:
pass
finally:
if not self.future.done():
self.future.cancel()
try:
await self.future
except asyncio.CancelledError:
pass
def stop(self, exceptions=True):
if not self.future.done():
self.future.cancel()
elif exceptions:
# Give a chance to raise any exceptions
self.future.result()
def __del__(self):
# Clean up on deletion
try:
self.stop(exceptions=False)
except RuntimeError:
# Event loop already stopped
pass
async def send_input(self, message):
"""
Sends a single message to the application
"""
# Give it the message
await self.input_queue.put(message)
async def receive_output(self, timeout=1):
"""
Receives a single message from the application, with optional timeout.
"""
# Make sure there's not an exception to raise from the task
if self.future.done():
self.future.result()
# Wait and receive the message
try:
async with async_timeout(timeout):
return await self.output_queue.get()
except asyncio.TimeoutError as e:
# See if we have another error to raise inside
if self.future.done():
self.future.result()
else:
self.future.cancel()
try:
await self.future
except asyncio.CancelledError:
pass
raise e
async def receive_nothing(self, timeout=0.1, interval=0.01):
"""
Checks that there is no message to receive in the given time.
"""
# `interval` has precedence over `timeout`
start = time.monotonic()
while time.monotonic() - start < timeout:
if not self.output_queue.empty():
return False
await asyncio.sleep(interval)
return self.output_queue.empty()

View File

@@ -1,127 +0,0 @@
# This code is originally sourced from the aio-libs project "async_timeout",
# under the Apache 2.0 license. You may see the original project at
# https://github.com/aio-libs/async-timeout
# It is vendored here to reduce chain-dependencies on this library, and
# modified slightly to remove some features we don't use.
import asyncio
import sys
from types import TracebackType
from typing import Any, Optional, Type
class timeout:
"""timeout context manager.
Useful in cases when you want to apply timeout logic around block
of code or in cases when asyncio.wait_for is not suitable. For example:
>>> with timeout(0.001):
... async with aiohttp.get('https://github.com') as r:
... await r.text()
timeout - value in seconds or None to disable timeout logic
loop - asyncio compatible event loop
"""
def __init__(
self,
timeout: Optional[float],
*,
loop: Optional[asyncio.AbstractEventLoop] = None,
) -> None:
self._timeout = timeout
if loop is None:
loop = asyncio.get_event_loop()
self._loop = loop
self._task = None # type: Optional[asyncio.Task[Any]]
self._cancelled = False
self._cancel_handler = None # type: Optional[asyncio.Handle]
self._cancel_at = None # type: Optional[float]
def __enter__(self) -> "timeout":
return self._do_enter()
def __exit__(
self,
exc_type: Type[BaseException],
exc_val: BaseException,
exc_tb: TracebackType,
) -> Optional[bool]:
self._do_exit(exc_type)
return None
async def __aenter__(self) -> "timeout":
return self._do_enter()
async def __aexit__(
self,
exc_type: Type[BaseException],
exc_val: BaseException,
exc_tb: TracebackType,
) -> None:
self._do_exit(exc_type)
@property
def expired(self) -> bool:
return self._cancelled
@property
def remaining(self) -> Optional[float]:
if self._cancel_at is not None:
return max(self._cancel_at - self._loop.time(), 0.0)
else:
return None
def _do_enter(self) -> "timeout":
# Support Tornado 5- without timeout
# Details: https://github.com/python/asyncio/issues/392
if self._timeout is None:
return self
self._task = current_task(self._loop)
if self._task is None:
raise RuntimeError(
"Timeout context manager should be used " "inside a task"
)
if self._timeout <= 0:
self._loop.call_soon(self._cancel_task)
return self
self._cancel_at = self._loop.time() + self._timeout
self._cancel_handler = self._loop.call_at(self._cancel_at, self._cancel_task)
return self
def _do_exit(self, exc_type: Type[BaseException]) -> None:
if exc_type is asyncio.CancelledError and self._cancelled:
self._cancel_handler = None
self._task = None
raise asyncio.TimeoutError
if self._timeout is not None and self._cancel_handler is not None:
self._cancel_handler.cancel()
self._cancel_handler = None
self._task = None
return None
def _cancel_task(self) -> None:
if self._task is not None:
self._task.cancel()
self._cancelled = True
def current_task(loop: asyncio.AbstractEventLoop) -> "Optional[asyncio.Task[Any]]":
if sys.version_info >= (3, 7):
task = asyncio.current_task(loop=loop)
else:
task = asyncio.Task.current_task(loop=loop)
if task is None:
# this should be removed, tokio must use register_task and family API
fn = getattr(loop, "current_task", None)
if fn is not None:
task = fn()
return task

View File

@@ -1,287 +0,0 @@
import sys
import warnings
from typing import (
Any,
Awaitable,
Callable,
Dict,
Iterable,
List,
Optional,
Tuple,
Type,
Union,
)
from asgiref._pep562 import pep562
if sys.version_info >= (3, 8):
from typing import Literal, Protocol, TypedDict
else:
from typing_extensions import Literal, Protocol, TypedDict
__all__ = (
"ASGIVersions",
"HTTPScope",
"WebSocketScope",
"LifespanScope",
"WWWScope",
"Scope",
"HTTPRequestEvent",
"HTTPResponseStartEvent",
"HTTPResponseBodyEvent",
"HTTPServerPushEvent",
"HTTPDisconnectEvent",
"WebSocketConnectEvent",
"WebSocketAcceptEvent",
"WebSocketReceiveEvent",
"WebSocketSendEvent",
"WebSocketResponseStartEvent",
"WebSocketResponseBodyEvent",
"WebSocketDisconnectEvent",
"WebSocketCloseEvent",
"LifespanStartupEvent",
"LifespanShutdownEvent",
"LifespanStartupCompleteEvent",
"LifespanStartupFailedEvent",
"LifespanShutdownCompleteEvent",
"LifespanShutdownFailedEvent",
"ASGIReceiveEvent",
"ASGISendEvent",
"ASGIReceiveCallable",
"ASGISendCallable",
"ASGI2Protocol",
"ASGI2Application",
"ASGI3Application",
"ASGIApplication",
)
class ASGIVersions(TypedDict):
spec_version: str
version: Union[Literal["2.0"], Literal["3.0"]]
class HTTPScope(TypedDict):
type: Literal["http"]
asgi: ASGIVersions
http_version: str
method: str
scheme: str
path: str
raw_path: bytes
query_string: bytes
root_path: str
headers: Iterable[Tuple[bytes, bytes]]
client: Optional[Tuple[str, int]]
server: Optional[Tuple[str, Optional[int]]]
extensions: Optional[Dict[str, Dict[object, object]]]
class WebSocketScope(TypedDict):
type: Literal["websocket"]
asgi: ASGIVersions
http_version: str
scheme: str
path: str
raw_path: bytes
query_string: bytes
root_path: str
headers: Iterable[Tuple[bytes, bytes]]
client: Optional[Tuple[str, int]]
server: Optional[Tuple[str, Optional[int]]]
subprotocols: Iterable[str]
extensions: Optional[Dict[str, Dict[object, object]]]
class LifespanScope(TypedDict):
type: Literal["lifespan"]
asgi: ASGIVersions
WWWScope = Union[HTTPScope, WebSocketScope]
Scope = Union[HTTPScope, WebSocketScope, LifespanScope]
class HTTPRequestEvent(TypedDict):
type: Literal["http.request"]
body: bytes
more_body: bool
class HTTPResponseStartEvent(TypedDict):
type: Literal["http.response.start"]
status: int
headers: Iterable[Tuple[bytes, bytes]]
class HTTPResponseBodyEvent(TypedDict):
type: Literal["http.response.body"]
body: bytes
more_body: bool
class HTTPServerPushEvent(TypedDict):
type: Literal["http.response.push"]
path: str
headers: Iterable[Tuple[bytes, bytes]]
class HTTPDisconnectEvent(TypedDict):
type: Literal["http.disconnect"]
class WebSocketConnectEvent(TypedDict):
type: Literal["websocket.connect"]
class WebSocketAcceptEvent(TypedDict):
type: Literal["websocket.accept"]
subprotocol: Optional[str]
headers: Iterable[Tuple[bytes, bytes]]
class WebSocketReceiveEvent(TypedDict):
type: Literal["websocket.receive"]
bytes: Optional[bytes]
text: Optional[str]
class WebSocketSendEvent(TypedDict):
type: Literal["websocket.send"]
bytes: Optional[bytes]
text: Optional[str]
class WebSocketResponseStartEvent(TypedDict):
type: Literal["websocket.http.response.start"]
status: int
headers: Iterable[Tuple[bytes, bytes]]
class WebSocketResponseBodyEvent(TypedDict):
type: Literal["websocket.http.response.body"]
body: bytes
more_body: bool
class WebSocketDisconnectEvent(TypedDict):
type: Literal["websocket.disconnect"]
code: int
class WebSocketCloseEvent(TypedDict):
type: Literal["websocket.close"]
code: int
reason: Optional[str]
class LifespanStartupEvent(TypedDict):
type: Literal["lifespan.startup"]
class LifespanShutdownEvent(TypedDict):
type: Literal["lifespan.shutdown"]
class LifespanStartupCompleteEvent(TypedDict):
type: Literal["lifespan.startup.complete"]
class LifespanStartupFailedEvent(TypedDict):
type: Literal["lifespan.startup.failed"]
message: str
class LifespanShutdownCompleteEvent(TypedDict):
type: Literal["lifespan.shutdown.complete"]
class LifespanShutdownFailedEvent(TypedDict):
type: Literal["lifespan.shutdown.failed"]
message: str
ASGIReceiveEvent = Union[
HTTPRequestEvent,
HTTPDisconnectEvent,
WebSocketConnectEvent,
WebSocketReceiveEvent,
WebSocketDisconnectEvent,
LifespanStartupEvent,
LifespanShutdownEvent,
]
ASGISendEvent = Union[
HTTPResponseStartEvent,
HTTPResponseBodyEvent,
HTTPServerPushEvent,
HTTPDisconnectEvent,
WebSocketAcceptEvent,
WebSocketSendEvent,
WebSocketResponseStartEvent,
WebSocketResponseBodyEvent,
WebSocketCloseEvent,
LifespanStartupCompleteEvent,
LifespanStartupFailedEvent,
LifespanShutdownCompleteEvent,
LifespanShutdownFailedEvent,
]
ASGIReceiveCallable = Callable[[], Awaitable[ASGIReceiveEvent]]
ASGISendCallable = Callable[[ASGISendEvent], Awaitable[None]]
class ASGI2Protocol(Protocol):
def __init__(self, scope: Scope) -> None:
...
async def __call__(
self, receive: ASGIReceiveCallable, send: ASGISendCallable
) -> None:
...
ASGI2Application = Type[ASGI2Protocol]
ASGI3Application = Callable[
[
Scope,
ASGIReceiveCallable,
ASGISendCallable,
],
Awaitable[None],
]
ASGIApplication = Union[ASGI2Application, ASGI3Application]
__deprecated__ = {
"WebsocketConnectEvent": WebSocketConnectEvent,
"WebsocketAcceptEvent": WebSocketAcceptEvent,
"WebsocketReceiveEvent": WebSocketReceiveEvent,
"WebsocketSendEvent": WebSocketSendEvent,
"WebsocketResponseStartEvent": WebSocketResponseStartEvent,
"WebsocketResponseBodyEvent": WebSocketResponseBodyEvent,
"WebsocketDisconnectEvent": WebSocketDisconnectEvent,
"WebsocketCloseEvent": WebSocketCloseEvent,
}
def __getattr__(name: str) -> Any:
deprecated = __deprecated__.get(name)
if deprecated:
stacklevel = 3 if sys.version_info >= (3, 7) else 4
warnings.warn(
f"'{name}' is deprecated. Use '{deprecated.__name__}' instead.",
category=DeprecationWarning,
stacklevel=stacklevel,
)
return deprecated
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
def __dir__() -> List[str]:
return sorted(list(__all__) + list(__deprecated__.keys()))
pep562(__name__)

View File

@@ -1,162 +0,0 @@
from io import BytesIO
from tempfile import SpooledTemporaryFile
from asgiref.sync import AsyncToSync, sync_to_async
class WsgiToAsgi:
"""
Wraps a WSGI application to make it into an ASGI application.
"""
def __init__(self, wsgi_application):
self.wsgi_application = wsgi_application
async def __call__(self, scope, receive, send):
"""
ASGI application instantiation point.
We return a new WsgiToAsgiInstance here with the WSGI app
and the scope, ready to respond when it is __call__ed.
"""
await WsgiToAsgiInstance(self.wsgi_application)(scope, receive, send)
class WsgiToAsgiInstance:
"""
Per-socket instance of a wrapped WSGI application
"""
def __init__(self, wsgi_application):
self.wsgi_application = wsgi_application
self.response_started = False
self.response_content_length = None
async def __call__(self, scope, receive, send):
if scope["type"] != "http":
raise ValueError("WSGI wrapper received a non-HTTP scope")
self.scope = scope
with SpooledTemporaryFile(max_size=65536) as body:
# Alright, wait for the http.request messages
while True:
message = await receive()
if message["type"] != "http.request":
raise ValueError("WSGI wrapper received a non-HTTP-request message")
body.write(message.get("body", b""))
if not message.get("more_body"):
break
body.seek(0)
# Wrap send so it can be called from the subthread
self.sync_send = AsyncToSync(send)
# Call the WSGI app
await self.run_wsgi_app(body)
def build_environ(self, scope, body):
"""
Builds a scope and request body into a WSGI environ object.
"""
environ = {
"REQUEST_METHOD": scope["method"],
"SCRIPT_NAME": scope.get("root_path", "").encode("utf8").decode("latin1"),
"PATH_INFO": scope["path"].encode("utf8").decode("latin1"),
"QUERY_STRING": scope["query_string"].decode("ascii"),
"SERVER_PROTOCOL": "HTTP/%s" % scope["http_version"],
"wsgi.version": (1, 0),
"wsgi.url_scheme": scope.get("scheme", "http"),
"wsgi.input": body,
"wsgi.errors": BytesIO(),
"wsgi.multithread": True,
"wsgi.multiprocess": True,
"wsgi.run_once": False,
}
# Get server name and port - required in WSGI, not in ASGI
if "server" in scope:
environ["SERVER_NAME"] = scope["server"][0]
environ["SERVER_PORT"] = str(scope["server"][1])
else:
environ["SERVER_NAME"] = "localhost"
environ["SERVER_PORT"] = "80"
if "client" in scope:
environ["REMOTE_ADDR"] = scope["client"][0]
# Go through headers and make them into environ entries
for name, value in self.scope.get("headers", []):
name = name.decode("latin1")
if name == "content-length":
corrected_name = "CONTENT_LENGTH"
elif name == "content-type":
corrected_name = "CONTENT_TYPE"
else:
corrected_name = "HTTP_%s" % name.upper().replace("-", "_")
# HTTPbis say only ASCII chars are allowed in headers, but we latin1 just in case
value = value.decode("latin1")
if corrected_name in environ:
value = environ[corrected_name] + "," + value
environ[corrected_name] = value
return environ
def start_response(self, status, response_headers, exc_info=None):
"""
WSGI start_response callable.
"""
# Don't allow re-calling once response has begun
if self.response_started:
raise exc_info[1].with_traceback(exc_info[2])
# Don't allow re-calling without exc_info
if hasattr(self, "response_start") and exc_info is None:
raise ValueError(
"You cannot call start_response a second time without exc_info"
)
# Extract status code
status_code, _ = status.split(" ", 1)
status_code = int(status_code)
# Extract headers
headers = [
(name.lower().encode("ascii"), value.encode("ascii"))
for name, value in response_headers
]
# Extract content-length
self.response_content_length = None
for name, value in response_headers:
if name.lower() == "content-length":
self.response_content_length = int(value)
# Build and send response start message.
self.response_start = {
"type": "http.response.start",
"status": status_code,
"headers": headers,
}
@sync_to_async
def run_wsgi_app(self, body):
"""
Called in a subthread to run the WSGI app. We encapsulate like
this so that the start_response callable is called in the same thread.
"""
# Translate the scope and incoming request body into a WSGI environ
environ = self.build_environ(self.scope, body)
# Run the WSGI app
bytes_sent = 0
for output in self.wsgi_application(environ, self.start_response):
# If this is the first response, include the response headers
if not self.response_started:
self.response_started = True
self.sync_send(self.response_start)
# If the application supplies a Content-Length header
if self.response_content_length is not None:
# The server should not transmit more bytes to the client than the header allows
bytes_allowed = self.response_content_length - bytes_sent
if len(output) > bytes_allowed:
output = output[:bytes_allowed]
self.sync_send(
{"type": "http.response.body", "body": output, "more_body": True}
)
bytes_sent += len(output)
# The server should stop iterating over the response when enough data has been sent
if bytes_sent == self.response_content_length:
break
# Close connection
if not self.response_started:
self.response_started = True
self.sync_send(self.response_start)
self.sync_send({"type": "http.response.body"})

View File

@@ -1,46 +0,0 @@
from django.contrib import admin
from django.contrib.admin.filters import (
SimpleListFilter,
AllValuesFieldListFilter,
ChoicesFieldListFilter,
RelatedFieldListFilter,
RelatedOnlyFieldListFilter
)
class InputFilter(admin.SimpleListFilter):
template = 'baton/filters/input_filter.html'
def lookups(self, request, model_admin):
# Dummy, required to show the filter.
return ((),)
def choices(self, changelist):
# Grab only the "all" option.
all_choice = next(super(InputFilter, self).choices(changelist))
all_choice['query_parts'] = (
(k, v)
for k, v in changelist.get_filters_params().items()
if k != self.parameter_name
)
yield all_choice
class SimpleDropdownFilter(SimpleListFilter):
template = 'baton/filters/dropdown_filter.html'
class DropdownFilter(AllValuesFieldListFilter):
template = 'baton/filters/dropdown_filter.html'
class ChoicesDropdownFilter(ChoicesFieldListFilter):
template = 'baton/filters/dropdown_filter.html'
class RelatedDropdownFilter(RelatedFieldListFilter):
template = 'baton/filters/dropdown_filter.html'
class RelatedOnlyDropdownFilter(RelatedOnlyFieldListFilter):
template = 'baton/filters/dropdown_filter.html'

View File

@@ -1,7 +0,0 @@
from __future__ import unicode_literals
from django.apps import AppConfig
class BatonConfig(AppConfig):
name = 'baton'

View File

@@ -1,41 +0,0 @@
# -*- coding: utf-8 -*-
from django.conf import settings
from django.utils.html import mark_safe
from django.utils.translation import gettext_lazy as _
default_config = {
'SITE_TITLE': 'Baton',
'SITE_HEADER': '<img src="%sbaton/img/logo.png" />' % settings.STATIC_URL,
'INDEX_TITLE': _('Site administration'),
'MENU_TITLE': _('Menu'),
'SUPPORT_HREF': 'https://github.com/otto-torino/django-baton/issues',
'COPYRIGHT': 'copyright © 2020 <a href="https://www.otto.to.it">Otto srl</a>', # noqa
'POWERED_BY': '<a href="https://www.otto.to.it">Otto srl</a>',
'CONFIRM_UNSAVED_CHANGES': True,
'SHOW_MULTIPART_UPLOADING': True,
'ENABLE_IMAGES_PREVIEW': True,
'COLLAPSABLE_USER_AREA': False,
'CHANGELIST_FILTERS_IN_MODAL': False,
'CHANGELIST_FILTERS_ALWAYS_OPEN': False,
'CHANGELIST_FILTERS_FORM': False,
'MENU_ALWAYS_COLLAPSED': False,
'MESSAGES_TOASTS': False,
'GRAVATAR_DEFAULT_IMG': 'retro',
'LOGIN_SPLASH': None,
'SEARCH_FIELD': None,
}
def get_config(key):
safe = ['SITE_HEADER', 'COPYRIGHT', 'POWERED_BY', ]
user_settings = getattr(settings, 'BATON', None)
if user_settings is None:
value = default_config.get(key, None)
else:
value = user_settings.get(key, default_config.get(key, None))
if key in safe:
return mark_safe(value)
return value

View File

@@ -1,134 +0,0 @@
# baton translations
# Copyright (C) 2017 Otto srl
# This file is distributed under the same license as the django-baton package.
# Stefano Contini <abidibo@gmail.com>, 2017.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: 0.1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-12-03 16:44+0100\n"
"PO-Revision-Date: 2017-02-12 17:00+0100\n"
"Last-Translator: Stefano Contini <abidibo@gmail.com>\n"
"Language-Team: it <LL@li.org>\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: config.py:9
msgid "Site administration"
msgstr "Amministrazione sito"
#: config.py:10
msgid "Menu"
msgstr "Menu"
#: templates/admin/base_site.html:5
msgid "Django site admin"
msgstr "Amministrazione sito django"
#: templates/admin/base_site.html:44
msgid "Django administration"
msgstr "Amministrazione django"
#: templates/admin/base_site.html:63
msgid "close"
msgstr "chiudi"
#: templates/admin/base_site.html:64
msgid "save"
msgstr "salva"
#: templates/admin/filer/folder/directory_listing.html:4
msgid "Folders"
msgstr "Cartelle"
#: templates/baton/analytics.html:24
msgid "last 15 days"
msgstr "ultimi 15 giorni"
#: templates/baton/analytics.html:25
msgid "last month"
msgstr "ultimo mese"
#: templates/baton/analytics.html:26
msgid "last three months"
msgstr "ultimi tre mesi"
#: templates/baton/analytics.html:27
msgid "last year"
msgstr "ultimo anno"
#: templates/baton/analytics.html:34
msgid "Traffic"
msgstr "Traffico"
#: templates/baton/analytics.html:35
msgid "Sessions and Users"
msgstr "Sessioni e utenti"
#: templates/baton/analytics.html:41
msgid "Popular"
msgstr "Popolare"
#: templates/baton/analytics.html:42
msgid "Page views"
msgstr "Visualizzazioni di pagina"
#: templates/baton/analytics.html:50
msgid "Browsers"
msgstr "Browser"
#: templates/baton/analytics.html:51
msgid "Top used"
msgstr "Più utilizzati"
#: templates/baton/analytics.html:57
msgid "Acquisition"
msgstr "Acquisizione"
#: templates/baton/analytics.html:58
msgid "Referral Traffic"
msgstr "Traffico da ricerca"
#: templates/baton/analytics.html:66
msgid "Audience"
msgstr "Visitatori"
#: templates/baton/analytics.html:67
msgid "Countries"
msgstr "Paesi"
#: templates/baton/analytics.html:73
msgid "Social"
msgstr "Social"
#: templates/baton/analytics.html:74
msgid "Interactions"
msgstr "Interazioni"
#: templates/baton/filters/dropdown_filter.html:3
#: templates/baton/filters/input_filter.html:3
#, python-format
msgid " By %(filter_title)s "
msgstr "Per %(filter_title)s"
#: templates/baton/filters/input_filter.html:16
msgid "type and press enter..."
msgstr "digita e premi invio..."
#: templates/baton/filters/input_filter.html:20
msgid "Remove"
msgstr "Rimuovi"
#: templates/baton/footer.html:7
msgid "Support"
msgstr "Supporto"
#: templates/baton/footer.html:18
#, python-format
msgid "Developed by %(powered_by)s"
msgstr "Sviluppato da %(powered_by)s"

View File

@@ -1,52 +0,0 @@
/* VARIABLE DEFINITIONS */
:root {
--primary: #79aec8;
--secondary: #417690;
--accent: #f5dd5d;
--primary-fg: #fff;
--body-fg: #333;
--body-bg: #fff;
--body-quiet-color: #666;
--body-loud-color: #000;
--header-color: #ffc;
--header-branding-color: var(--accent);
--header-bg: var(--secondary);
--header-link-color: var(--primary-fg);
--breadcrumbs-fg: #c4dce8;
--breadcrumbs-link-fg: var(--body-bg);
--breadcrumbs-bg: var(--primary);
--link-fg: #447e9b;
--link-hover-color: #036;
--link-selected-fg: #5b80b2;
--hairline-color: #e8e8e8;
--border-color: #ccc;
--error-fg: #ba2121;
--message-success-bg: #dfd;
--message-warning-bg: #ffc;
--message-error-bg: #ffefef;
--darkened-bg: #f8f8f8; /* A bit darker than --body-bg */
--selected-bg: #e4e4e4; /* E.g. selected table cells */
--selected-row: #ffc;
--button-fg: #fff;
--button-bg: var(--primary);
--button-hover-bg: #609ab6;
--default-button-bg: var(--secondary);
--default-button-hover-bg: #205067;
--close-button-bg: #888; /* Previously #bbb, contrast 1.92 */
--close-button-hover-bg: #747474;
--delete-button-bg: #ba2121;
--delete-button-hover-bg: #a41515;
--object-tools-fg: var(--button-fg);
--object-tools-bg: var(--close-button-bg);
--object-tools-hover-bg: var(--close-button-hover-bg);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +0,0 @@
{
"presets": ["@babel/preset-env"]
}

View File

@@ -1,2 +0,0 @@
node_modules/**
dist/**

View File

@@ -1,24 +0,0 @@
{
"parser" : "babel-eslint",
"extends" : [
"standard"
],
"plugins" : [
"flow-vars"
],
"env" : {
"browser" : true
},
"globals" : {
"Baton": {},
"jQuery": {},
"$": {},
},
"rules": {
"semi" : [2, "never"],
"max-len": [2, 120, 2],
"comma-dangle": [2, "only-multiline"],
"flow-vars/define-flow-type": 1,
"flow-vars/use-flow-type": 1
}
}

View File

@@ -1,13 +0,0 @@
{
"libs": [
"browser",
"ecma5",
"ecma6"
],
"plugins": {
"node": { },
"modules": { },
"complete_strings": {},
"es_modules": {}
}
}

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 898 KiB

View File

@@ -1,3 +0,0 @@
<svg width="15" height="15" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
<path fill="#555555" d="M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z"/>
</svg>

Before

Width:  |  Height:  |  Size: 458 B

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 730 KiB

View File

@@ -1,34 +0,0 @@
<svg width="16" height="192" viewBox="0 0 1792 21504" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="up">
<path d="M1412 895q0-27-18-45l-362-362-91-91q-18-18-45-18t-45 18l-91 91-362 362q-18 18-18 45t18 45l91 91q18 18 45 18t45-18l189-189v502q0 26 19 45t45 19h128q26 0 45-19t19-45v-502l189 189q19 19 45 19t45-19l91-91q18-18 18-45zm252 1q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="down">
<path d="M1412 897q0-27-18-45l-91-91q-18-18-45-18t-45 18l-189 189v-502q0-26-19-45t-45-19h-128q-26 0-45 19t-19 45v502l-189-189q-19-19-45-19t-45 19l-91 91q-18 18-18 45t18 45l362 362 91 91q18 18 45 18t45-18l91-91 362-362q18-18 18-45zm252-1q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="left">
<path d="M1408 960v-128q0-26-19-45t-45-19h-502l189-189q19-19 19-45t-19-45l-91-91q-18-18-45-18t-45 18l-362 362-91 91q-18 18-18 45t18 45l91 91 362 362q18 18 45 18t45-18l91-91q18-18 18-45t-18-45l-189-189h502q26 0 45-19t19-45zm256-64q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="right">
<path d="M1413 896q0-27-18-45l-91-91-362-362q-18-18-45-18t-45 18l-91 91q-18 18-18 45t18 45l189 189h-502q-26 0-45 19t-19 45v128q0 26 19 45t45 19h502l-189 189q-19 19-19 45t19 45l91 91q18 18 45 18t45-18l362-362 91-91q18-18 18-45zm251 0q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="clearall">
<path transform="translate(336, 336) scale(0.75)" d="M1037 1395l102-102q19-19 19-45t-19-45l-307-307 307-307q19-19 19-45t-19-45l-102-102q-19-19-45-19t-45 19l-454 454q-19 19-19 45t19 45l454 454q19 19 45 19t45-19zm627-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="chooseall">
<path transform="translate(336, 336) scale(0.75)" d="M845 1395l454-454q19-19 19-45t-19-45l-454-454q-19-19-45-19t-45 19l-102 102q-19 19-19 45t19 45l307 307-307 307q-19 19-19 45t19 45l102 102q19 19 45 19t45-19zm819-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
</defs>
<use xlink:href="#up" x="0" y="0" fill="#666666" />
<use xlink:href="#up" x="0" y="1792" fill="#447e9b" />
<use xlink:href="#down" x="0" y="3584" fill="#666666" />
<use xlink:href="#down" x="0" y="5376" fill="#447e9b" />
<use xlink:href="#left" x="0" y="7168" fill="#666666" />
<use xlink:href="#left" x="0" y="8960" fill="#447e9b" />
<use xlink:href="#right" x="0" y="10752" fill="#666666" />
<use xlink:href="#right" x="0" y="12544" fill="#447e9b" />
<use xlink:href="#clearall" x="0" y="14336" fill="#666666" />
<use xlink:href="#clearall" x="0" y="16128" fill="#447e9b" />
<use xlink:href="#chooseall" x="0" y="17920" fill="#666666" />
<use xlink:href="#chooseall" x="0" y="19712" fill="#447e9b" />
</svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -1,19 +0,0 @@
<svg width="14" height="84" viewBox="0 0 1792 10752" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="sort">
<path d="M1408 1088q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45zm0-384q0 26-19 45t-45 19h-896q-26 0-45-19t-19-45 19-45l448-448q19-19 45-19t45 19l448 448q19 19 19 45z"/>
</g>
<g id="ascending">
<path d="M1408 1216q0 26-19 45t-45 19h-896q-26 0-45-19t-19-45 19-45l448-448q19-19 45-19t45 19l448 448q19 19 19 45z"/>
</g>
<g id="descending">
<path d="M1408 704q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45z"/>
</g>
</defs>
<use xlink:href="#sort" x="0" y="0" fill="#999999" />
<use xlink:href="#sort" x="0" y="1792" fill="#447e9b" />
<use xlink:href="#ascending" x="0" y="3584" fill="#999999" />
<use xlink:href="#ascending" x="0" y="5376" fill="#447e9b" />
<use xlink:href="#descending" x="0" y="7168" fill="#999999" />
<use xlink:href="#descending" x="0" y="8960" fill="#447e9b" />
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,14 +0,0 @@
<svg width="15" height="60" viewBox="0 0 1792 7168" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="previous">
<path d="M1037 1395l102-102q19-19 19-45t-19-45l-307-307 307-307q19-19 19-45t-19-45l-102-102q-19-19-45-19t-45 19l-454 454q-19 19-19 45t19 45l454 454q19 19 45 19t45-19zm627-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
<g id="next">
<path d="M845 1395l454-454q19-19 19-45t-19-45l-454-454q-19-19-45-19t-45 19l-102 102q-19 19-19 45t19 45l307 307-307 307q-19 19-19 45t19 45l102 102q19 19 45 19t45-19zm819-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</g>
</defs>
<use xlink:href="#previous" x="0" y="0" fill="#333333" />
<use xlink:href="#previous" x="0" y="1792" fill="#000000" />
<use xlink:href="#next" x="0" y="3584" fill="#333333" />
<use xlink:href="#next" x="0" y="5376" fill="#000000" />
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

View File

@@ -1,30 +0,0 @@
/*!
* Bootstrap v5.0.1 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
/*!
* Sizzle CSS Selector Engine v2.3.6
* https://sizzlejs.com/
*
* Copyright JS Foundation and other contributors
* Released under the MIT license
* https://js.foundation/
*
* Date: 2021-02-16
*/
/*!
* jQuery JavaScript Library v3.6.0
* https://jquery.com/
*
* Includes Sizzle.js
* https://sizzlejs.com/
*
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*
* Date: 2021-03-02T17:08Z
*/

View File

@@ -1,3 +0,0 @@
<svg width="13" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
<path fill="#666666" d="M1024 1376v-192q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23-9t9-23zm256-672q0-88-55.5-163t-138.5-116-170-41q-243 0-371 213-15 24 8 42l132 100q7 6 19 6 16 0 25-12 53-68 86-92 34-24 86-24 48 0 85.5 26t37.5 59q0 38-20 61t-68 45q-63 28-115.5 86.5t-52.5 125.5v36q0 14 9 23t23 9h192q14 0 23-9t9-23q0-19 21.5-49.5t54.5-49.5q32-18 49-28.5t46-35 44.5-48 28-60.5 12.5-81zm384 192q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
</svg>

Before

Width:  |  Height:  |  Size: 655 B

File diff suppressed because it is too large Load Diff

View File

@@ -1,54 +0,0 @@
{
"name": "baton",
"version": "2.2.3",
"description": "Django Baton App",
"main": "index.js",
"scripts": {
"stats": "npx webpack --config ./webpack.prod.js --json > stats.json",
"compile": "BATON_REVISION=$(git rev-parse HEAD) npx webpack --config ./webpack.prod.js",
"watch": "npx webpack --watch --config ./webpack.dev.js",
"dev": "BATON_REVISION=$(git rev-parse HEAD) webpack serve --host 0.0.0.0 --progress --config ./webpack.dev.js",
"lint": "./node_modules/eslint/bin/eslint.js ."
},
"author": "abidibo",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.14.3",
"@babel/preset-env": "^7.14.4",
"@babel/register": "^7.13.16",
"@fortawesome/fontawesome-free": "^5.15.3",
"autoprefixer": "^10.2.6",
"babel-cli": "^6.26.0",
"babel-loader": "^8.2.2",
"babel-register": "^6.26.0",
"bootstrap": "^5.0.1",
"css-loader": "^5.2.6",
"file-loader": "^6.2.0",
"ini": "^2.0.0",
"jquery": "^3.6.0",
"js-event-dispatcher": "^0.1.0",
"loader": "^2.1.1",
"lodash": "^4.17.21",
"mini-svg-data-uri": "^1.3.3",
"node-sass": "^6.0.0",
"postcss-loader": "^5.3.0",
"sass": "^1.34.0",
"sass-loader": "^11.1.1",
"style-loader": "^2.0.0",
"url-loader": "^4.1.1",
"webpack": "^5.38.1",
"webpack-dev-server": "^3.11.2"
},
"devDependencies": {
"babel-eslint": "^10.1.0",
"eslint": "^7.27.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-babel": "^5.3.1",
"eslint-plugin-flow-vars": "^0.5.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-standard": "^5.0.0",
"webpack-bundle-analyzer": "^4.4.2",
"webpack-cli": "^4.7.0",
"webpack-merge": "^5.7.3"
}
}

View File

@@ -1,5 +0,0 @@
module.exports = {
plugins: [
require('autoprefixer')
]
}

View File

@@ -1,6 +0,0 @@
set smarttab
set shiftwidth=2
set softtabstop=2
set tabstop=2
set expandtab
let syntastic_scss_checkers = ['scss_lint']

View File

@@ -1,14 +0,0 @@
import $ from 'jquery'
let ActionResult = {
/**
* Menu component
*
* Adds a sidebar menu to the document
*/
init: function () {
$('body').addClass('actionresult')
}
}
export default ActionResult

View File

@@ -1,15 +0,0 @@
import $ from 'jquery'
let AdminDocs = {
/**
* Footer component
*
* Moves the footer inside the main external container
*/
init: function (opts) {
let container = $('<div />', {'class': 'admindocs-body'})
container.append($('#content > *:not(h1):not(.breadcrumbs)')).appendTo($('#content'))
}
}
export default AdminDocs

View File

@@ -1,214 +0,0 @@
import Translator from './i18n'
class Analytics {
constructor (gapi, token, viewId, domIds) {
this.gapi = gapi
this.token = token
this.domIds = domIds
this.viewId = viewId
this.t = new Translator($('html').attr('lang'))
}
init (timedelta) {
let self = this
this.gapi.analytics.ready(function () { self.run(timedelta) })
}
run (timedelta) {
let self = this
let gapi = this.gapi
let spinner = $('<div />').css({
textAlign: 'center',
padding: '3rem 0',
color: '#aaa',
}).append($('<i />', {'class': 'fa fa-spinner fa-spin fa-3x fa-fw'}))
for (let prop in this.domIds) {
if (['traffic', 'popular', 'browsers', 'acquisition', 'audience', 'social'].indexOf(prop) !== -1) {
$('#' + this.domIds[prop]).append(spinner.clone())
}
}
let errorCb = containerId => () => {
$('#' + containerId).empty()
let message = $('<div />').css({
textAlign: 'center',
padding: '3rem 0',
color: '#aaa',
}).text(this.t.get('retrieveDataError'))
$('#' + containerId).append(message)
}
gapi.analytics.ready(function () {
/**
* Authorize the user with an access token obtained server side.
*/
gapi.analytics.auth.authorize({
'serverAuth': {
'access_token': self.token
}
})
/**
* Create a new ViewSelector2 instance to be rendered inside of an
* element with the id "view-selector-container".
*/
var viewSelector = new gapi.analytics.ViewSelector({
container: self.domIds.viewSelector,
})
viewSelector.execute()
let baseQuery = {
'ids': 'ga:' + self.viewId,
'start-date': timedelta,
'end-date': 'yesterday'
}
/**
* Creates a new DataChart instance showing sessions over the past 15 days.
*/
let dataChart1 = new gapi.analytics.googleCharts.DataChart({
query: {
...baseQuery,
'metrics': 'ga:sessions,ga:users',
'dimensions': 'ga:date'
},
chart: {
'container': self.domIds.traffic,
'type': 'LINE',
'options': {
'width': '100%'
}
}
})
let trafficTimeout = setTimeout(errorCb(self.domIds.traffic), 20000)
dataChart1.on('error', errorCb(self.domIds.traffic))
dataChart1.on('success', () => clearTimeout(trafficTimeout))
dataChart1.execute()
/**
* Creates a new DataChart instance showing top 5 most popular pages
*/
var dataChart2 = new gapi.analytics.googleCharts.DataChart({
query: {
...baseQuery,
'metrics': 'ga:pageviews',
'dimensions': 'ga:pagePath',
'sort': '-ga:pageviews',
'max-results': 7
},
chart: {
'container': self.domIds.popular,
'type': 'PIE',
'options': {
'width': '100%',
'pieHole': 4 / 9,
}
}
})
let popularTimeout = setTimeout(errorCb(self.domIds.popular), 20000)
dataChart2.on('error', errorCb(self.domIds.popular))
dataChart2.on('success', () => clearTimeout(popularTimeout))
dataChart2.execute()
/**
* Creates a new DataChart instance showing top borwsers
*/
var dataChart3 = new gapi.analytics.googleCharts.DataChart({
query: {
...baseQuery,
'metrics': 'ga:sessions',
'dimensions': 'ga:browser',
'sort': '-ga:sessions',
'max-results': 7
},
chart: {
'container': self.domIds.browsers,
'type': 'PIE',
'options': {
'width': '100%',
'pieHole': 4 / 9,
}
}
})
let browsersTimeout = setTimeout(errorCb(self.domIds.browsers), 20000)
dataChart3.on('error', errorCb(self.domIds.browsers))
dataChart3.on('success', () => clearTimeout(browsersTimeout))
dataChart3.execute()
/**
* Creates a new DataChart instance showing top referral
*/
var dataChart4 = new gapi.analytics.googleCharts.DataChart({
query: {
...baseQuery,
'metrics': 'ga:sessions',
'dimensions': 'ga:source',
'sort': '-ga:sessions',
'max-results': 7
},
chart: {
'container': self.domIds.acquisition,
'type': 'PIE',
'options': {
'width': '100%',
'pieHole': 4 / 9,
}
}
})
let acquisitionTimeout = setTimeout(errorCb(self.domIds.acquisition), 20000)
dataChart4.on('error', errorCb(self.domIds.acquisition))
dataChart4.on('success', () => clearTimeout(acquisitionTimeout))
dataChart4.execute()
/**
* Creates a new DataChart instance showing top visitors continents
*/
var dataChart5 = new gapi.analytics.googleCharts.DataChart({
query: {
...baseQuery,
'metrics': 'ga:sessions',
'dimensions': 'ga:country',
'sort': '-ga:sessions',
'max-results': 7
},
chart: {
'container': self.domIds.audience,
'type': 'PIE',
'options': {
'width': '100%',
'pieHole': 4 / 9,
}
}
})
let audienceTimeout = setTimeout(errorCb(self.domIds.audience), 20000)
dataChart5.on('error', errorCb(self.domIds.audience))
dataChart5.on('success', () => clearTimeout(audienceTimeout))
dataChart5.execute()
/**
* Creates a new DataChart instance showing social interactions over the past 15 days.
*/
var dataChart6 = new gapi.analytics.googleCharts.DataChart({
query: {
...baseQuery,
'metrics': 'ga:socialInteractions',
'dimensions': 'ga:socialInteractionNetwork',
'sort': '-ga:socialInteractions',
'max-results': 7
},
chart: {
'container': self.domIds.social,
'type': 'PIE',
'options': {
'width': '100%',
'pieHole': 4 / 9,
}
}
})
let socialTimeout = setTimeout(errorCb(self.domIds.social), 20000)
dataChart6.on('error', errorCb(self.domIds.social))
dataChart6.on('success', () => clearTimeout(socialTimeout))
dataChart6.execute()
})
}
}
export default Analytics

View File

@@ -1,7 +0,0 @@
export default {
sm: 576,
md: 768,
lg: 992,
xl: 1200,
xxl: 1400
}

View File

@@ -1,193 +0,0 @@
import $ from 'jquery'
import Translator from './i18n'
let ChangeForm = {
/**
* ChangeForm component
*
* Alert unsaved changes stuff
* Display loading spinner if multipart
*/
init: function (opts) {
var self = this
this.form = $('#content-main form')
if (opts.confirmUnsavedChanges) {
this.formSubmitting = false
this.t = new Translator($('html').attr('lang'))
// wait for django SelectFilter to do its job
setTimeout(function () {
self.initData = self.serializeData()
self.activate()
}, 500)
}
if (opts.showMultipartUploading) {
this.spinner()
}
self.fixNewlines()
setTimeout(function () {
self.fixNewlines() // js inserted ones
}, 50)
this.fixWrappedFields()
if (opts.enableImagesPreview) {
this.lazyLoadImages()
}
this.activateEntryCollapsing()
this.changeFieldsetCollapseStyle()
this.fixExpandFirstErrorCollapsing()
this.initTemplates()
},
activate: function () {
this.form.on('submit', () => (this.formSubmitting = true))
$(window).on('beforeunload', this.alertDirty.bind(this))
},
serializeData: function () {
// form serialize does not detect filter_horizontal controllers because
// in that case options are not selected, just added to the list of options,
// and jquery form serialize only serializes values which are set!
let data = this.form.serialize()
$('select.filtered[multiple][id$=_to]').each(function (k, select) {
let optionsValues = []
$(select).children('option').each(function (kk, option) {
optionsValues.push($(option).attr('value'))
})
data += `&${jQuery(select).attr('name')}=${optionsValues.sort().join(',')}`
})
return data
},
isDirty: function () {
return this.serializeData() !== this.initData
},
alertDirty: function (e) {
if (this.formSubmitting || !this.isDirty()) {
return undefined
}
let confirmationMessage = this.t.get('unsavedChangesAlert');
(e || window.event).returnValue = confirmationMessage // Gecko + IE
return confirmationMessage // Gecko + Webkit, Safari, Chrome etc.
},
spinner: function () {
if (this.form.attr('enctype') === 'multipart/form-data') {
this.form.on('submit', () => this.showSpinner())
}
},
showSpinner: function () {
let run = false
let inputFiles = $('input[type=file]')
inputFiles.each(function (index, input) {
if (input.files.length !== 0) {
run = true
}
})
if (run) {
let overlay = $('<div />', {'class': 'spinner-overlay'}).appendTo(document.body)
let spinner = $('<i />', {'class': 'fa fa-spinner fa-spin fa-3x fa-fw'})
$('<div />').append(
$('<p />').text(this.t.get('uploading')),
spinner
).appendTo(overlay)
}
},
fixWrappedFields: function () {
this.form.find('.form-row').each(function (index, row) {
var fieldBoxes = $(row).children('.fieldBox')
fieldBoxes.each(function (index, fbox) {
if ($(fbox).hasClass('errors')) {
$(row).addClass('errors')
}
})
fieldBoxes.wrapAll('<div class="wrapped-fields-container" />')
if (fieldBoxes.length) {
$(row).addClass('with-wrapped-fields')
}
})
// this.form.find('.wrapped-fields-container > .fieldBox:first-child').children().unwrap()
},
fixNewlines: function () {
$('.form-row br').replaceWith('<span class="newline"></span>')
},
lazyLoadImages: function () {
$('.file-upload').each(function (index, p) {
let cur = $(p).find('a')
if (cur.length) {
let url = cur.attr('href')
let ext = url.split('?')[0].split('.').pop()
if (['jpg', 'jpeg', 'png', 'bmp', 'svg', 'gif', 'tif', 'webp'].indexOf(ext) !== -1) {
let spinner = $('<i />', {'class': 'fa fa-spinner fa-spin fa-2x fa-fw'}).css('color', '#aaa')
let preview = $('<div />', {'class': 'py-2'}).append(spinner)
$(p).prepend(preview)
let image = new Image()
image.onload = function () {
spinner.replaceWith($(image).addClass('baton-image-preview'))
}
image.onerror = function () {
preview.remove()
}
image.src = url
}
}
})
},
activateEntryCollapsing: function () {
$('.collapse-entry h3')
.addClass('entry-collapsed entry-collapse-full-toggler')
.append('<span />') // just to have the toggler right aligned
.append('<span class="entry-collapse-toggler" />')
$('.collapse-entry')
.click(function (e) {
let target = $(e.target)
if (target.hasClass('entry-collapse-full-toggler')) {
target.toggleClass('entry-collapsed')
} else if (target.parent('.entry-collapse-full-toggler').length > 0) {
target.parent('.entry-collapse-full-toggler').toggleClass('entry-collapsed')
}
})
$('.form-row.errors').each(function (index, el) {
if ($(el).parent('fieldset').prev('h3.entry-collapsed')) {
$(el).parent('fieldset').prev('h3.entry-collapsed').removeClass('entry-collapsed')
}
})
},
fixExpandFirstErrorCollapsing: function () {
$('.expand-first').each(function (index, el) {
if ($(el).find('.inline-related[id$=0] .form-row.errors').length) {
// inverse logic
$(el).find('.inline-related[id$=0] .form-row.errors').parent('fieldset').prev('h3').addClass('entry-collapsed')
}
})
},
changeFieldsetCollapseStyle: function () {
$(window).on('load', function () {
$('fieldset.collapse > h2').each(function (index, title) {
let text = $(title).text().replace(/\(.*\)/, '')
setTimeout(function () {
$(title).html(text).on('click', function () {
$(this).parent('.collapse').toggleClass('collapsed')
})
}, 100)
})
})
},
initTemplates: function () {
const positionMap = {
above: 'before',
below: 'after',
top: 'prepend',
bottom: 'append',
right: 'after'
}
$('template').each(function (index, template) {
let field = $(template).attr('id').replace('template-', '')
let position = positionMap[$(template).attr('data-position')]
if (position !== undefined) {
let el = $(template).attr('data-position') === 'right'
? $('.form-row.field-' + field + ' #id_' + field)
: $('.form-row.field-' + field)
el[position]($(template).html())
} else {
console.error('Baton: wrong form include position detected')
}
})
}
}
export default ChangeForm

View File

@@ -1,203 +0,0 @@
import $ from 'jquery'
import Translator from './i18n'
import Modal from './Modal'
import breakpoints from './Breakpoints'
let ChangeList = {
/**
* ChangeList component
*
* Filtering stuff
*/
init: function (opts) {
this._filtersDiv = $('#changelist-filter')
this.t = new Translator($('html').attr('lang'))
this.filtersForm = opts.changelistFiltersForm
this.filtersInModal = opts.changelistFiltersInModal
this.filtersAlwaysOpen = opts.changelistFiltersAlwaysOpen
this.initTemplates()
if (this._filtersDiv.length) {
var self = this
setTimeout(function () {
self.activate()
}, 200) // select2
this.fixRangeFilter()
}
},
activate: function () {
if ($('.changelist-form-container').length) {
// django >= 3.1
$('#changelist-filter').appendTo($('.changelist-form-container'))
}
let isModal = false
if (this.filtersAlwaysOpen) {
$(document.body).addClass(
'changelist-filter-active changelist-filter-always-open'
)
} else {
// filters active?
let _activeFilters = /__[^=]+=/.test(location.search)
// actions ?
let _activeActions = $('#changelist-form > .actions').length !== 0
let _changelistForm = $('#changelist-form')
let _filtersToggler = $('<a />', {
class:
'changelist-filter-toggler' +
(_activeFilters ? ' active' : '') +
(_activeActions ? ' with-actions' : '')
}).html(
'<i class="fa fa-filter"></i> <span>' + this.t.get('filter') + '</span>'
)
if (this.filtersInModal || parseInt($(window).width()) < breakpoints.lg) {
let self = this
isModal = true
// wait for filters used js to exec
$('#changelist-filter').prop('id', 'changelist-filter-modal')
let titleEl = $('#changelist-filter-modal > h2')
let title = titleEl.html()
titleEl.remove()
let content = $('#changelist-filter-modal')
// remove from dom
this.modal = new Modal({
title,
content,
size: 'md',
hideFooter: !this.filtersForm,
actionBtnLabel: this.t.get('filter'),
actionBtnCb: function () { self.filter(content) }
})
_filtersToggler.click(() => {
self.modal.toggle()
})
} else {
_filtersToggler.click(() => {
$(document.body).toggleClass('changelist-filter-active')
if (parseInt(this._filtersDiv.css('max-width')) === 100) {
// diff between mobile and lg
$('html,body').animate({
scrollTop: this._filtersDiv.offset().top
})
}
})
}
_changelistForm.prepend(_filtersToggler)
}
if (!isModal && this.filtersForm) {
// add filters button
let btn = $('<a />', {'class': 'btn btn-primary'}).html(this.t.get('filter'))
.on('click', () => this.filter($('#changelist-filter')))
$('#changelist-filter').append($('<div />', {'class': 'text-center mb-3'}).append(btn))
}
if (/_popup=1/.test(location.href)) {
$('#changelist-form .results').css('padding-top', '78px')
}
},
getDropdownValue: function (dropdown) {
let items = $(dropdown).find('option').attr('value').substr(1).split('&')
let values = $(dropdown).val().substr(1).split('&').filter(item => items.indexOf(item) === -1)
return values.length ? values.join('&') : null
},
filter: function (wrapper) {
var self = this
let qs = []
let dropdowns = wrapper.find('select')
let textInputs = wrapper.find('input').not('[type=hidden]')
dropdowns
.toArray()
.map(el => self.getDropdownValue(el))
.filter(v => v !== null)
.forEach(v => qs.push(v))
textInputs.each((idx, el) => el.value !== '' ? qs.push(`${el.name}=${el.value}`) : null)
// console.log(location.pathname + (qs.length ? '?' + qs.filter(q => q !== '').join('&') : ''), qs)
location.href = location.pathname + (qs.length ? '?' + qs.filter(q => q !== '').join('&') : '')
},
initTemplates: function () {
const positionMap = {
above: 'before',
below: 'after',
top: 'prepend',
bottom: 'append'
}
$('template[data-type=include]').each(function (index, template) {
let position = positionMap[$(template).attr('data-position')]
if (position !== undefined) {
let el = $('#changelist-form')
el[position]($(template).html())
} else {
console.error('Baton: wrong changelist include position detected')
}
})
$('template[data-type=filters-include]').each(function (index, template) {
let position = positionMap[$(template).attr('data-position')]
if (
position !== undefined &&
position !== 'before' &&
position !== 'after'
) {
if (position === 'prepend' && $('#changelist-filter-clear').length) {
$('#changelist-filter-clear').after($(template).html())
} else if (
position === 'prepend' &&
$('#changelist-filter > h2').length
) {
$('#changelist-filter > h2').after($(template).html())
} else {
let el = $('#changelist-filter')
el[position]($(template).html())
}
} else {
console.error(
'Baton: wrong changelist filters include position detected'
)
}
})
$('template[data-type=attributes]').each(function (index, template) {
try {
let data = JSON.parse($(template).html())
for (let key in data) {
if (data.hasOwnProperty(key)) {
let selector
let getParent = 'tr'
if (data[key]['selector']) {
selector = data[key]['selector']
delete data[key]['selector']
} else {
selector =
'#result_list tr input[name=_selected_action][value=' +
key +
']'
}
if (data[key]['getParent'] !== undefined) {
getParent = data[key]['getParent']
delete data[key]['getParent']
}
let el = getParent ? $(selector).parents(getParent) : $(selector)
el.attr(data[key])
}
}
} catch (e) {
console.error(e)
}
})
},
fixRangeFilter: function () {
if (this.filtersForm) {
$('.admindatefilter .controls').remove()
$('.admindatefilter form').onSubmit = function () { return false }
}
}
}
export default ChangeList

View File

@@ -1,30 +0,0 @@
import $ from 'jquery'
import { copyTextToClipboard } from './Utils'
let Filer = {
/**
* ChangeList component
*
* Filtering stuff
*/
init: function (opts) {
this.fixIcons()
this.fixCopyToClipboard()
},
fixIcons: function () {
$('.fa-pencil').addClass('fa-pencil-alt')
},
fixCopyToClipboard: function () {
let copyBtns = $('.action-button .fa-link')
copyBtns.on('click', function (evt) {
evt.preventDefault()
var link = $(this).parent('.action-button').attr('href')
if (!link) {
link = $(this).parent('.action-button').next('.action-button').attr('href')
}
copyTextToClipboard(link)
})
}
}
export default Filer

View File

@@ -1,17 +0,0 @@
import $ from 'jquery'
let Footer = {
/**
* Footer component
*
* Moves the footer inside the main external container
*/
init: function (opts) {
$('#footer').appendTo('#content')
if (opts.remove) {
$('#footer').remove()
}
}
}
export default Footer

View File

@@ -1,47 +0,0 @@
import $ from 'jquery'
const Login = {
init: function (config) {
// splash
if (config.loginSplash) {
$('body.login').css({
background: `url(${config.loginSplash}) no-repeat center center`,
backgroundSize: 'cover'
})
}
// form
let inputUsername = $('#id_username')
let inputPassword = $('#id_password')
const usernameField = $('<div />', { class: 'input-group mb-2' })
.append(
$('<span />', { class: 'input-group-text' }).append(
'<i class="fa fa-user"></i>'
)
)
.append(inputUsername.clone())
inputUsername.replaceWith(usernameField)
// adds show/hide password functionality
let passwordInputField = inputPassword.clone()
let viewPasswordIcon = $('<i />', {'class': 'fa fa-eye pwd-visibility-toggle'}).on('click', function () {
let visible = $(this).hasClass('fa-eye-slash')
$(this)[visible ? 'removeClass' : 'addClass']('fa-eye-slash')
passwordInputField.attr('type', visible ? 'password' : 'text')
})
const passwordField = $('<div />', { class: 'input-group mb-2' })
.append(
$('<span />', { class: 'input-group-text' }).append(
'<i class="fa fa-key"></i>'
)
)
.append(passwordInputField)
.append(viewPasswordIcon)
inputPassword.replaceWith(passwordField)
}
}
export default Login

View File

@@ -1,375 +0,0 @@
import $ from 'jquery'
import Translator from './i18n'
let Menu = {
/**
* Menu component
*
* Adds a sidebar menu to the document
*/
init: function (config, Dispatcher) {
this.Dispatcher = Dispatcher
this.t = new Translator($('html').attr('lang'))
this.collapsableUserArea = config.collapsableUserArea
this.menuTitle = config.menuTitle
this.searchField = config.searchField
this.appListUrl = config.api.app_list
this.gravatarUrl = config.api.gravatar
this.gravatarDefaultImg = config.gravatarDefaultImg
this.alwaysCollapsed = $('#header').hasClass('menu-always-collapsed')
this.fixNodes()
this.brandingClone = $('#branding').clone()
this.manageBrandingUserTools()
this.manageSearchField()
this.fetchData()
this.setHeight()
let self = this
$(window).on('resize', function () {
self.setHeight()
self.manageBrandingUserTools()
})
},
fixNodes: function () {
let container = $('<div/>', { class: 'container-fluid' })
$('#footer').before(container)
let row = $('<div/>', { class: 'row' }).appendTo(container)
this.menu = $('<nav/>', { class: 'col-lg-2 sidebar-menu' }).appendTo(row)
$('#content')
.addClass('col-lg-10')
.prepend($('.breadcrumbs'))
.appendTo(row)
$('#content > h1').after($('.messagelist'))
let title = $('<h1 />', { class: 'd-block d-lg-none' }).text(
this.menuTitle ? this.menuTitle : 'Menu'
)
$('<i/>', { class: 'fa fa-times' })
.click(() => {
$(document.body).removeClass('menu-open')
})
.appendTo(title)
this.menu.append(title)
if (this.alwaysCollapsed) {
let close = $('<i />', { class: 'fa fa-times toggle-menu' })
.appendTo(this.menu)
.click(() => {
$(document.body).removeClass('menu-open')
})
}
},
manageBrandingUserTools: function () {
if (parseInt($(window).width()) >= 992) {
// move user tools
this.menu.prepend($('#user-tools'))
if (this.alwaysCollapsed) {
// copy branding
this.menu.prepend(this.brandingClone)
} else {
// move branding
this.menu.prepend($('#branding'))
}
if ($('#user-tools-sidebar').length === 0) {
this.renderUserTools()
}
} else {
$('#header').append($('#user-tools'))
if (this.alwaysCollapsed) {
this.menu.find('#branding').remove()
} else {
$('#header .navbar-toggler').after($('#branding'))
}
if ($('#user-tools-sidebar').length === 0) {
this.removeUserTools()
}
}
},
manageSearchField () {
// unset
if (!this.searchField || !this.searchField.url) {
return
}
let container = $('<div />', { class: 'search-field-tool' })
let field = $('<input />', {
class: 'form-control form-control-sm',
type: 'text',
list: 'admin-search-datalist',
placeholder: this.searchField.label || this.t('search')
})
let dataList = $('<div />', { id: 'admin-search-datalist' }).on('mouseover', e => {
if ($(e.target).hasClass('datalist-option') || $(e.target).parent('.datalist-option').length) {
dataList.find('.datalist-option').removeClass('selected')
let item = $(e.target).hasClass('datalist-option') ? $(e.target) : $(e.target).parent('.datalist-option')
item.addClass('selected')
}
})
let navigateDataList = code => {
let target
let active = dataList.find('.datalist-option.selected').first()
if (!active.length) {
target = dataList.find('.datalist-option')[code === 40 ? 'first' : 'last']()
} else {
if (code === 40) {
let next = active.next()
target = next.length ? next : dataList.find('.datalist-option').first()
} else {
let prev = active.prev()
target = prev.length ? prev : dataList.find('.datalist-option').last()
}
}
if (target) {
active.removeClass('selected')
$(target).addClass('selected')
target[0].scrollIntoView({
behavior: 'smooth',
block: 'end',
inline: 'nearest'
})
}
}
field.on('blur', e => setTimeout(() => dataList.hide(), 150))
field.on('focus', e => dataList.show())
field.on('keyup', e => {
var code = e.keyCode || e.which
if (code === 13) {
// goto url if there is an active voice
let active = dataList.find('.datalist-option.selected').first()
if (active.length) {
location.href = active.attr('data-url')
}
return
}
if ([40, 38].indexOf(code) !== -1) {
// move
navigateDataList(code)
} else {
// search
if ($(field).val().length < 1) {
dataList.empty()
return
}
container.addClass('loading')
$.getJSON(this.searchField.url, { text: $(field).val() })
.done(data => {
container.removeClass('loading')
dataList.empty()
data.data.forEach((r, index) => dataList.append(`
<div class="datalist-option${index === 0 ? ' selected' : ''}" onclick="location.href='${r.url}'" data-url="${r.url}"><a href="${r.url}">${r.label}</a>${r.icon ? `<i onclick="location.href='${r.url}'" class="${r.icon}"></i>` : ''}</div>`)
)
})
.fail((jqxhr, textStatus, err) => {
console.log(err)
container.removeClass('loading')
dataList.empty()
})
}
})
$('#user-tools-sidebar').after(container.append([field, dataList]))
},
renderUserTools: function () {
let self = this
let container = $('<div />', { id: 'user-tools-sidebar' })
let expandUserArea = $('<i />', {'class': 'fa fa-angle-down user-area-toggler'}).on('click', function () {
$(this).toggleClass('fa-angle-up')
container.toggleClass('collapsed')
})
if (this.collapsableUserArea) {
container.addClass('collapsed')
}
container.insertAfter('#user-tools')
let userInfo = $('<div />', { class: 'user-info' })
.html(
'<div class="spinner-border text-primary" role="status"><span class="sr-only">Loading...</span></div><div>' +
$('#user-tools .dropdown-toggle').text() +
'</div>'
)
.appendTo(container)
// gravatar
$.getJSON(this.gravatarUrl, function (data) {
let img = $('<img />', {
class: 'gravatar-icon',
src: 'https://www.gravatar.com/avatar/{hash}?s=50&d={default}'
.replace('{hash}', data.hash)
.replace('{default}', self.gravatarDefaultImg)
})
userInfo.find('.spinner-border').replaceWith(img)
if (self.collapsableUserArea) {
img.after(expandUserArea)
}
}).fail(function (err) {
console.error(err.responseText)
let img = $('<img />', {
class: 'gravatar-icon',
src: 'https://www.gravatar.com/avatar/{hash}?s=50&d={default}'
.replace('{hash}', '')
.replace('{default}', self.gravatarDefaultImg)
})
userInfo.find('.spinner-border').replaceWith(img)
if (self.collapsableUserArea) {
img.after(expandUserArea)
}
})
let linksContainer = $('<div />', { class: 'user-links' }).appendTo(
container
)
$('#user-tools .dropdown-menu a').each(function (index, el) {
let cls = 'view-site'
if (/password_change/.test($(el).attr('href'))) {
cls = 'password'
} else if (/logout/.test($(el).attr('href'))) {
cls = 'logout'
}
let text = $(el).text()
let clone = $(el)
.clone()
.html('')
.attr('class', cls)
.attr('title', text)
if (cls === 'view-site') {
clone.attr('target', '_blank')
}
linksContainer.append(clone)
})
},
removeUserTools: function () {
$('#user-tools-sidebar').remove()
},
fetchData: function () {
let self = this
$.getJSON(this.appListUrl, function (data) {
self.render(data)
self.Dispatcher.emit('onMenuReady')
}).fail(function (err) {
console.error(err.responseText)
self.menu.remove()
$('#content')
.removeClass('col-md-9')
.removeClass('col-lg-10')
.css('flex-grow', 1)
self.Dispatcher.emit('onMenuError')
})
},
setHeight: function () {
let height = $(window).height() - $('#header').height() - 19 // nav padding and border
this.menu.css('min-height', height + 'px')
$('#content').css('padding-bottom', $('#footer').height() + 20 + 'px')
},
render: function (data) {
let self = this
let mainUl = $('<ul/>', { class: 'depth-0' }).appendTo(self.menu)
data.forEach((voice, index) => {
let active = false
if (voice.type === 'free') {
if (voice.re) {
let re = new RegExp(voice.re)
active = re.test(location.pathname)
} else {
active = location.pathname === voice.url
}
} else {
if (voice.url) {
let pathRexp = new RegExp(voice.url)
active = pathRexp.test(location.pathname)
}
}
let li = $('<li />', {
class:
'top-level ' +
voice.type +
(voice.defaultOpen ? ' default-open' : '') +
(active ? ' active' : '')
})
let a = $('<' + (voice.url ? 'a' : 'span') + ' />', {
href: voice.url || '#'
})
.text(voice.label)
.appendTo(li)
// icon
if (voice.icon) {
$('<i />', { class: voice.icon }).prependTo(a)
}
let subUl
if (voice.children && voice.children.length) {
subUl = $('<ul />', { class: 'depth-1' }).appendTo(li)
a.addClass('has-children')
voice.children.forEach((model, i) => {
let active = false
if (model.type === 'free') {
if (model.re) {
let re = new RegExp(model.re)
active = re.test(location.pathname)
} else {
active = location.pathname === model.url
}
} else if (model.url) {
let pathRexp = new RegExp(model.url)
active = pathRexp.test(location.pathname)
}
let subLi = $('<li />')
if (active) {
subLi.addClass('active')
li.addClass('with-active')
}
let a = $('<a />', {
href: model.url
})
.text(model.label)
.appendTo(subLi)
// icon
if (model.icon) {
$('<i />', { class: model.icon }).prependTo(a)
}
subLi.appendTo(subUl)
})
}
li.appendTo(mainUl)
})
$('.has-children').on('click', function (evt) {
evt.preventDefault()
let self = this
let p = $(this).parent()
let depth0 = $('.depth-0')
let depth1 = p.children('ul')
if (
p.hasClass('open') ||
(p.hasClass('default-open') && !$('body').hasClass('menu-open'))
) {
p.removeClass('open default-open')
depth1.children('.nav-back').remove()
depth0.css('overflow', 'auto')
} else {
if (p.hasClass('top-level')) {
$('.top-level').removeClass('open')
$('.top-level')
.find('.nav-back')
.remove()
}
p.addClass('open')
let back = $(
'<li class="nav-item nav-back"><a href="#"><i class="fa fa-angle-double-left"></i> ' + // eslint-disable-line
$(this).text() +
'</a></li>'
)
back.on('click', function () {
$(self).trigger('click')
})
depth1.prepend(back)
depth0.css('overflow', 'hidden')
depth0.scrollTop(0) // return to top
}
})
}
}
export default Menu

View File

@@ -1,72 +0,0 @@
import $ from 'jquery'
import bootstrap from 'bootstrap/dist/js/bootstrap.bundle'
let Messages = {
/**
* Messages
*
* If toast are enabled, moves messages ul lis in toasts
*/
init: function (opts) {
if (opts.messagesToasts) {
let toasts = []
let all = true
$('.messagelist li').each((index, el) => {
let lv = $(el).attr('class')
if (opts.messagesToasts === true || opts.messagesToasts.indexOf(lv) !== -1) {
toasts.push(this.createToast($(el).attr('class'), $(el).html()))
$(el).remove()
} else {
all = false
}
})
if (toasts.length) {
$('<div />', {'class': 'toast-container position-absolute top-0 end-0 p-3'})
.append(toasts).appendTo($(document.body))
}
if (all) {
$('.messagelist').remove()
}
}
const toastElList = [].slice.call(document.querySelectorAll('.toast'))
toastElList.map(function (toastEl) {
new bootstrap.Toast(toastEl, { autohide: false }).show()
})
},
levelsMap: {
info: {
bg: 'info',
icon: 'fa fa-info-circle',
iconColor: '#fff'
},
success: {
bg: 'success',
icon: 'fa fa-check-circle',
iconColor: '#fff'
},
warning: {
bg: 'warning',
icon: 'fa fa-exclamation-circle',
iconColor: '#fff'
},
error: {
bg: 'danger',
icon: 'fa fa-exclamation-circle',
iconColor: '#fff'
}
},
createToast (level, content) {
let toast = `
<div class="toast d-flex align-items-center text-white bg-${this.levelsMap[level].bg} border-0" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-body">
<i class="${this.levelsMap[level].icon}" style="color: ${this.levelsMap[level].iconColor}; margin-right: .5rem"></i>
${content}
</div>
<button type="button" class="btn-close btn-close-white ms-auto me-2" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
`
return $(toast)
}
}
export default Messages

View File

@@ -1,155 +0,0 @@
import $ from 'jquery'
import Translator from './i18n'
import bootstrap from 'bootstrap/dist/js/bootstrap.bundle'
class Modal {
constructor (config) {
this.t = new Translator($('html').attr('lang'))
this.opts = {
subtitle: '',
hideFooter: false,
showBackBtn: false,
backBtnCb: function () {},
actionBtnLabel: this.t.get('save'),
actionBtnCb: null,
onUrlLoaded: function () {},
size: 'lg',
onClose: function () {}
}
this.isOpen = false
this.create() // adds modal, modalObj and events
this.update(config)
}
create () {
this.modalObj = $('<div />', {'class': 'modal fade'}).appendTo(document.body)
this.modalObj.html(`
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"></h5>
<div style="display: flex;">
<button type="button" class="back me-1" aria-label="Back">
<i class="fa fa-angle-left"></i>
</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">${this.t.get('close')}</button>
<button type="button" class="btn btn-primary btn-action"></button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
`)
var self = this
this.modalObj.on('hidden.bs.modal', function () {
self.close()
})
this.modal = new bootstrap.Modal(this.modalObj[0])
}
update (config) {
this.options = $.extend({}, this.opts, config)
this.setSize()
this.setHeader()
this.setTitle()
this.setSubtitle()
this.setContent()
this.setButtons()
}
setSize () {
this.modalObj.find('.modal-dialog').addClass('modal-' + this.options.size)
}
setHeader () {
if (this.options.showBackBtn) {
this.modalObj.find('.modal-header .back').show()
this.modalObj
.find('.modal-header .back')
.on('click', this.options.backBtnCb)
} else {
this.modalObj.find('.modal-header .back').hide()
}
}
setTitle () {
if (typeof this.options.title !== 'undefined') {
this.modalObj.find('.modal-title').html(this.options.title)
}
}
setSubtitle () {
if (this.options.subtitle) {
this.modalObj
.find('.modal-subtitle')
.show()
.html(this.options.subtitle)
} else {
this.modalObj
.find('.modal-subtitle')
.hide()
.html('')
}
}
setContent () {
var self = this
if (typeof this.options.url !== 'undefined') {
this.method = 'request'
$.get(this.options.url, function (response) {
self.modalObj.find('.modal-body').html(response)
self.options.onUrlLoaded(self)
})
} else if (this.options.content instanceof jQuery) {
self.modalObj.find('.modal-body').append(this.options.content)
} else if (typeof this.options.content !== 'undefined') {
self.modalObj.find('.modal-body').html(this.options.content)
}
};
setButtons () {
if (this.options.hideFooter) {
this.modalObj.find('.modal-footer').hide()
} else {
if (this.options.actionBtnCb) {
this.modalObj.find('.btn-action').text(this.options.actionBtnLabel)
this.modalObj.find('.btn-action').on('click', this.options.actionBtnCb)
} else {
this.modalObj.find('.btn-action').hide()
}
}
}
open () {
if (this.isOpen) {
return
}
this.toggle()
this.isOpen = true
}
toggle () {
this.modal[this.isOpen ? 'hide' : 'show']()
this.isOpen = !this.isOpen
}
close () {
if (!this.isOpen) {
return
}
this.modal.hide()
this.options.onClose()
this.isOpen = false
}
}
export default Modal

View File

@@ -1,44 +0,0 @@
import $ from 'jquery'
let Navbar = {
/**
* Navbar component
*
* Adds a menu toggler for mobile and does some styling
*/
init: function (config) {
this.menuAlwaysCollapsed = config.menuAlwaysCollapsed
this.fixNodes()
},
fixNodes: function () {
if (!this.menuAlwaysCollapsed) {
$('#header').addClass('expand')
} else {
$('#header').addClass('menu-always-collapsed')
}
// insert burger
$('#branding').before(
$('<button/>', {
'class': 'navbar-toggler navbar-toggler-right',
'data-bs-toggle': 'collapse'
}).html('<i class="fa fa-bars"></i>')
.click(() => $(document.body).addClass('menu-open')))
// remove only text
$('#user-tools')
.contents().filter(function () {
return (this.nodeType === 3)
}).remove()
// dropdown
let dropdown = $('<div/>', { 'class': 'dropdown' }).appendTo($('#user-tools'))
let dropdownMenu = $('<div/>', { 'class': 'dropdown-menu dropdown-menu-right' }).appendTo(dropdown)
$('#user-tools strong')
.addClass('dropdown-toggle btn btn-default')
.attr('data-bs-toggle', 'dropdown')
.prependTo(dropdown)
// @TODO find a way to mv view site from dropdown
// password change view doesn't have it so breaks things
$('#user-tools > a').addClass('dropdown-item').appendTo(dropdownMenu)
}
}
export default Navbar

View File

@@ -1,9 +0,0 @@
import $ from 'jquery'
let PasswordChange = {
init: function () {
$('body').addClass('passwordchange')
}
}
export default PasswordChange

View File

@@ -1,163 +0,0 @@
import $ from 'jquery'
import bootstrap from 'bootstrap/dist/js/bootstrap.bundle'
let Tabs = {
/**
* Tabs component
*/
init: function (Dispatcher) {
this.Dispatcher = Dispatcher
if (this.shouldRun()) {
console.info('Baton:', 'generating tabs')
this.main.attr('data-baton-tab', 'main-tab')
this.createNav()
this.createPanes()
this.checkHash()
this.showErrors()
this.Dispatcher.emit('onTabsReady')
}
},
shouldRun: function () {
this.main = $('#content-main form .baton-tabs-init')
return this.main.length === 1
},
createNav: function () {
this.mainOrder = 0
this.tabsEl = []
this.domTabsEl = []
let classes = this.main.attr('class')
classes.split(' ').forEach((cl) => {
if (/baton-tab-/.test(cl)) {
this.tabsEl.push(cl.substring(10))
}
if (/order-/.test(cl)) {
this.mainOrder = parseInt(cl.replace('order-', ''))
}
})
let currentOrder = this.mainOrder ? 0 : this.mainOrder + 1
this.nav = $('<ul />', { 'class': 'nav nav-tabs' })
$('<li />', { 'class': 'nav-item' })
.css('order', this.mainOrder)
.append($('<a />', {
'class': 'nav-link' + (this.mainOrder === 0 ? ' active' : ''),
'data-bs-toggle': 'tab',
'data-bs-target': '#main-tab'
// href: '#main-tab'
}).text(this.main.children('h2').hide().text()))
.appendTo(this.nav)
this.tabsEl.forEach((el) => {
let domEl
if (/^group-/.test(el)) {
domEl = $('<div />').attr('data-baton-tab', el)
let items = el.substr(6).split('--')
items.forEach((item) => {
let e
if (/^inline-/.test(item)) {
e = this.createInlineEl(item)
} else {
e = this.createFieldsetEl(item)
}
domEl.append(e)
})
} else if (/^inline-/.test(el)) {
domEl = this.createInlineEl(el, true)
} else {
domEl = this.createFieldsetEl(el, true)
}
this.domTabsEl.push(domEl)
$('<li />', { 'class': 'nav-item ' })
.css('order', currentOrder)
.append($('<a />', {
'class': 'nav-link ' + (currentOrder === 0 ? ' active' : ''),
'data-bs-toggle': 'tab',
'data-bs-target': '#' + el
// href: '#' + el
}).text(domEl.find('h2:first-child').first().hide().text()))
.appendTo(this.nav)
currentOrder += 1
if (currentOrder === this.mainOrder) {
currentOrder += 1
}
})
this.main.before(this.nav)
$('a[data-bs-toggle="tab"]').on('shown.bs.tab', function (e) {
// add hash to stay in same tab when save and continue
const hash = $(e.target).attr('data-bs-target')
window.location.replace(hash) // adding with replace won't add an history entry
let tooltipTriggerList = [].slice.call($('[title]:not(iframe)'))
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
})
},
createInlineEl: function (el, setDataTab = false) {
let domEl
if ($('#' + el.substring(7) + '_set-group').length) { // no related_name
domEl = $('#' + el.substring(7) + '_set-group')
} else {
domEl = $('#' + el.substring(7) + '-group')
}
if (setDataTab) {
domEl.attr('data-baton-tab', el)
}
return domEl
},
createFieldsetEl: function (el, setDataTab = false) {
let domEl = $('.tab-' + el)
if (setDataTab) {
domEl.attr('data-baton-tab', el)
}
return domEl
},
createPanes: function () {
let self = this
this.tabContent = $('<div />', { 'class': 'tab-content' })
this.tabMain = $('<div />', {
'class': 'tab-pane fade' + (this.mainOrder === 0 ? ' active show' : ''),
'id': 'main-tab'
}).appendTo(this.tabContent)
this.main.parent().children(':not(.nav-tabs):not(.submit-row):not(.errornote):not(.tab-fs-none)')
.each((index, el) => {
$(el).appendTo(self.tabMain)
})
this.nav.after(this.tabContent)
let currentOrder = this.mainOrder ? 0 : this.mainOrder + 1
this.domTabsEl.forEach((el, index) => {
let tabPane = $('<div />', {
'class': 'tab-pane' + (currentOrder === 0 ? ' active show' : ''),
'id': self.tabsEl[index]
}).appendTo(this.tabContent)
el.appendTo(tabPane)
currentOrder += 1
if (currentOrder === this.mainOrder) {
currentOrder += 1
}
})
},
showErrors: function () {
let els = [this.main, ...this.domTabsEl]
for (let i = 0, len = els.length; i < len; i++) {
let el = els[i]
if (el.find('.form-row.errors, .errorlist').length) {
const tab = new bootstrap.Tab(this.nav.find('a[data-bs-target="#' + el.attr('data-baton-tab') + '"]')[0])
tab.show()
break
}
}
},
checkHash: function () {
if (location.hash && this.nav.find('a[data-bs-target="' + location.hash + '"]').length) {
const tab = new bootstrap.Tab(this.nav.find('a[data-bs-target="' + location.hash + '"]')[0])
tab.show()
}
}
}
export default Tabs

View File

@@ -1,600 +0,0 @@
/**
* @license
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
// minimal template polyfill
(function() {
'use strict';
var needsTemplate = (typeof HTMLTemplateElement === 'undefined');
var brokenDocFragment = !(document.createDocumentFragment().cloneNode() instanceof DocumentFragment);
var needsDocFrag = false;
// NOTE: Replace DocumentFragment to work around IE11 bug that
// causes children of a document fragment modified while
// there is a mutation observer to not have a parentNode, or
// have a broken parentNode (!?!)
if (/Trident/.test(navigator.userAgent)) {
(function() {
needsDocFrag = true;
var origCloneNode = Node.prototype.cloneNode;
Node.prototype.cloneNode = function cloneNode(deep) {
var newDom = origCloneNode.call(this, deep);
if (this instanceof DocumentFragment) {
newDom.__proto__ = DocumentFragment.prototype;
}
return newDom;
};
// IE's DocumentFragment querySelector code doesn't work when
// called on an element instance
DocumentFragment.prototype.querySelectorAll = HTMLElement.prototype.querySelectorAll;
DocumentFragment.prototype.querySelector = HTMLElement.prototype.querySelector;
Object.defineProperties(DocumentFragment.prototype, {
'nodeType': {
get: function () {
return Node.DOCUMENT_FRAGMENT_NODE;
},
configurable: true
},
'localName': {
get: function () {
return undefined;
},
configurable: true
},
'nodeName': {
get: function () {
return '#document-fragment';
},
configurable: true
}
});
var origInsertBefore = Node.prototype.insertBefore;
function insertBefore(newNode, refNode) {
if (newNode instanceof DocumentFragment) {
var child;
while ((child = newNode.firstChild)) {
origInsertBefore.call(this, child, refNode);
}
} else {
origInsertBefore.call(this, newNode, refNode);
}
return newNode;
}
Node.prototype.insertBefore = insertBefore;
var origAppendChild = Node.prototype.appendChild;
Node.prototype.appendChild = function appendChild(child) {
if (child instanceof DocumentFragment) {
insertBefore.call(this, child, null);
} else {
origAppendChild.call(this, child);
}
return child;
};
var origRemoveChild = Node.prototype.removeChild;
var origReplaceChild = Node.prototype.replaceChild;
Node.prototype.replaceChild = function replaceChild(newChild, oldChild) {
if (newChild instanceof DocumentFragment) {
insertBefore.call(this, newChild, oldChild);
origRemoveChild.call(this, oldChild);
} else {
origReplaceChild.call(this, newChild, oldChild);
}
return oldChild;
};
Document.prototype.createDocumentFragment = function createDocumentFragment() {
var frag = this.createElement('df');
frag.__proto__ = DocumentFragment.prototype;
return frag;
};
var origImportNode = Document.prototype.importNode;
Document.prototype.importNode = function importNode(impNode, deep) {
deep = deep || false;
var newNode = origImportNode.call(this, impNode, deep);
if (impNode instanceof DocumentFragment) {
newNode.__proto__ = DocumentFragment.prototype;
}
return newNode;
};
})();
}
// NOTE: we rely on this cloneNode not causing element upgrade.
// This means this polyfill must load before the CE polyfill and
// this would need to be re-worked if a browser supports native CE
// but not <template>.
var capturedCloneNode = Node.prototype.cloneNode;
var capturedCreateElement = Document.prototype.createElement;
var capturedImportNode = Document.prototype.importNode;
var capturedRemoveChild = Node.prototype.removeChild;
var capturedAppendChild = Node.prototype.appendChild;
var capturedReplaceChild = Node.prototype.replaceChild;
var capturedParseFromString = DOMParser.prototype.parseFromString;
var capturedHTMLElementInnerHTML = Object.getOwnPropertyDescriptor(window.HTMLElement.prototype, 'innerHTML') || {
/**
* @this {!HTMLElement}
* @return {string}
*/
get: function() {
return this.innerHTML;
},
/**
* @this {!HTMLElement}
* @param {string}
*/
set: function(text) {
this.innerHTML = text;
}
};
var capturedChildNodes = Object.getOwnPropertyDescriptor(window.Node.prototype, 'childNodes') || {
/**
* @this {!Node}
* @return {!NodeList}
*/
get: function() {
return this.childNodes;
}
};
var elementQuerySelectorAll = Element.prototype.querySelectorAll;
var docQuerySelectorAll = Document.prototype.querySelectorAll;
var fragQuerySelectorAll = DocumentFragment.prototype.querySelectorAll;
var scriptSelector = 'script:not([type]),script[type="application/javascript"],script[type="text/javascript"]';
function QSA(node, selector) {
// IE 11 throws a SyntaxError with `scriptSelector` if the node has no children due to the `:not([type])` syntax
if (!node.childNodes.length) {
return [];
}
switch (node.nodeType) {
case Node.DOCUMENT_NODE:
return docQuerySelectorAll.call(node, selector);
case Node.DOCUMENT_FRAGMENT_NODE:
return fragQuerySelectorAll.call(node, selector);
default:
return elementQuerySelectorAll.call(node, selector);
}
}
// returns true if nested templates cannot be cloned (they cannot be on
// some impl's like Safari 8 and Edge)
// OR if cloning a document fragment does not result in a document fragment
var needsCloning = (function() {
if (!needsTemplate) {
var t = document.createElement('template');
var t2 = document.createElement('template');
t2.content.appendChild(document.createElement('div'));
t.content.appendChild(t2);
var clone = t.cloneNode(true);
return (clone.content.childNodes.length === 0 || clone.content.firstChild.content.childNodes.length === 0
|| brokenDocFragment);
}
})();
var TEMPLATE_TAG = 'template';
var PolyfilledHTMLTemplateElement = function() {};
if (needsTemplate) {
var contentDoc = document.implementation.createHTMLDocument('template');
var canDecorate = true;
var templateStyle = document.createElement('style');
templateStyle.textContent = TEMPLATE_TAG + '{display:none;}';
var head = document.head;
head.insertBefore(templateStyle, head.firstElementChild);
/**
Provides a minimal shim for the <template> element.
*/
PolyfilledHTMLTemplateElement.prototype = Object.create(HTMLElement.prototype);
// if elements do not have `innerHTML` on instances, then
// templates can be patched by swizzling their prototypes.
var canProtoPatch =
!(document.createElement('div').hasOwnProperty('innerHTML'));
/**
The `decorate` method moves element children to the template's `content`.
NOTE: there is no support for dynamically adding elements to templates.
*/
PolyfilledHTMLTemplateElement.decorate = function(template) {
// if the template is decorated or not in HTML namespace, return fast
if (template.content ||
template.namespaceURI !== document.documentElement.namespaceURI) {
return;
}
template.content = contentDoc.createDocumentFragment();
var child;
while ((child = template.firstChild)) {
capturedAppendChild.call(template.content, child);
}
// NOTE: prefer prototype patching for performance and
// because on some browsers (IE11), re-defining `innerHTML`
// can result in intermittent errors.
if (canProtoPatch) {
template.__proto__ = PolyfilledHTMLTemplateElement.prototype;
} else {
template.cloneNode = function(deep) {
return PolyfilledHTMLTemplateElement._cloneNode(this, deep);
};
// add innerHTML to template, if possible
// Note: this throws on Safari 7
if (canDecorate) {
try {
defineInnerHTML(template);
defineOuterHTML(template);
} catch (err) {
canDecorate = false;
}
}
}
// bootstrap recursively
PolyfilledHTMLTemplateElement.bootstrap(template.content);
};
// Taken from https://github.com/jquery/jquery/blob/73d7e6259c63ac45f42c6593da8c2796c6ce9281/src/manipulation/wrapMap.js
var topLevelWrappingMap = {
'option': ['select'],
'thead': ['table'],
'col': ['colgroup', 'table'],
'tr': ['tbody', 'table'],
'th': ['tr', 'tbody', 'table'],
'td': ['tr', 'tbody', 'table']
};
var getTagName = function(text) {
// Taken from https://github.com/jquery/jquery/blob/73d7e6259c63ac45f42c6593da8c2796c6ce9281/src/manipulation/var/rtagName.js
return ( /<([a-z][^/\0>\x20\t\r\n\f]+)/i.exec(text) || ['', ''])[1].toLowerCase();
};
var defineInnerHTML = function defineInnerHTML(obj) {
Object.defineProperty(obj, 'innerHTML', {
get: function() {
return getInnerHTML(this);
},
set: function(text) {
// For IE11, wrap the text in the correct (table) context
var wrap = topLevelWrappingMap[getTagName(text)];
if (wrap) {
for (var i = 0; i < wrap.length; i++) {
text = '<' + wrap[i] + '>' + text + '</' + wrap[i] + '>';
}
}
contentDoc.body.innerHTML = text;
PolyfilledHTMLTemplateElement.bootstrap(contentDoc);
while (this.content.firstChild) {
capturedRemoveChild.call(this.content, this.content.firstChild);
}
var body = contentDoc.body;
// If we had wrapped, get back to the original node
if (wrap) {
for (var j = 0; j < wrap.length; j++) {
body = body.lastChild;
}
}
while (body.firstChild) {
capturedAppendChild.call(this.content, body.firstChild);
}
},
configurable: true
});
};
var defineOuterHTML = function defineOuterHTML(obj) {
Object.defineProperty(obj, 'outerHTML', {
get: function() {
return '<' + TEMPLATE_TAG + '>' + this.innerHTML + '</' + TEMPLATE_TAG + '>';
},
set: function(innerHTML) {
if (this.parentNode) {
contentDoc.body.innerHTML = innerHTML;
var docFrag = this.ownerDocument.createDocumentFragment();
while (contentDoc.body.firstChild) {
capturedAppendChild.call(docFrag, contentDoc.body.firstChild);
}
capturedReplaceChild.call(this.parentNode, docFrag, this);
} else {
throw new Error("Failed to set the 'outerHTML' property on 'Element': This element has no parent node.");
}
},
configurable: true
});
};
defineInnerHTML(PolyfilledHTMLTemplateElement.prototype);
defineOuterHTML(PolyfilledHTMLTemplateElement.prototype);
/**
The `bootstrap` method is called automatically and "fixes" all
<template> elements in the document referenced by the `doc` argument.
*/
PolyfilledHTMLTemplateElement.bootstrap = function bootstrap(doc) {
var templates = QSA(doc, TEMPLATE_TAG);
for (var i=0, l=templates.length, t; (i<l) && (t=templates[i]); i++) {
PolyfilledHTMLTemplateElement.decorate(t);
}
};
// auto-bootstrapping for main document
document.addEventListener('DOMContentLoaded', function() {
PolyfilledHTMLTemplateElement.bootstrap(document);
});
// Patch document.createElement to ensure newly created templates have content
Document.prototype.createElement = function createElement() {
var el = capturedCreateElement.apply(this, arguments);
if (el.localName === 'template') {
PolyfilledHTMLTemplateElement.decorate(el);
}
return el;
};
DOMParser.prototype.parseFromString = function() {
var el = capturedParseFromString.apply(this, arguments);
PolyfilledHTMLTemplateElement.bootstrap(el);
return el;
};
Object.defineProperty(HTMLElement.prototype, 'innerHTML', {
get: function() {
return getInnerHTML(this);
},
set: function(text) {
capturedHTMLElementInnerHTML.set.call(this, text);
PolyfilledHTMLTemplateElement.bootstrap(this);
},
configurable: true,
enumerable: true
});
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#escapingString
var escapeAttrRegExp = /[&\u00A0"]/g;
var escapeDataRegExp = /[&\u00A0<>]/g;
var escapeReplace = function(c) {
switch (c) {
case '&':
return '&amp;';
case '<':
return '&lt;';
case '>':
return '&gt;';
case '"':
return '&quot;';
case '\u00A0':
return '&nbsp;';
}
};
var escapeAttr = function(s) {
return s.replace(escapeAttrRegExp, escapeReplace);
};
var escapeData = function(s) {
return s.replace(escapeDataRegExp, escapeReplace);
};
var makeSet = function(arr) {
var set = {};
for (var i = 0; i < arr.length; i++) {
set[arr[i]] = true;
}
return set;
};
// http://www.whatwg.org/specs/web-apps/current-work/#void-elements
var voidElements = makeSet([
'area',
'base',
'br',
'col',
'command',
'embed',
'hr',
'img',
'input',
'keygen',
'link',
'meta',
'param',
'source',
'track',
'wbr'
]);
var plaintextParents = makeSet([
'style',
'script',
'xmp',
'iframe',
'noembed',
'noframes',
'plaintext',
'noscript'
]);
/**
* @param {Node} node
* @param {Node} parentNode
* @param {Function=} callback
*/
var getOuterHTML = function(node, parentNode, callback) {
switch (node.nodeType) {
case Node.ELEMENT_NODE: {
var tagName = node.localName;
var s = '<' + tagName;
var attrs = node.attributes;
for (var i = 0, attr; (attr = attrs[i]); i++) {
s += ' ' + attr.name + '="' + escapeAttr(attr.value) + '"';
}
s += '>';
if (voidElements[tagName]) {
return s;
}
return s + getInnerHTML(node, callback) + '</' + tagName + '>';
}
case Node.TEXT_NODE: {
var data = /** @type {Text} */ (node).data;
if (parentNode && plaintextParents[parentNode.localName]) {
return data;
}
return escapeData(data);
}
case Node.COMMENT_NODE: {
return '<!--' + /** @type {Comment} */ (node).data + '-->';
}
default: {
window.console.error(node);
throw new Error('not implemented');
}
}
};
/**
* @param {Node} node
* @param {Function=} callback
*/
var getInnerHTML = function(node, callback) {
if (node.localName === 'template') {
node = /** @type {HTMLTemplateElement} */ (node).content;
}
var s = '';
var c$ = callback ? callback(node) : capturedChildNodes.get.call(node);
for (var i=0, l=c$.length, child; (i<l) && (child=c$[i]); i++) {
s += getOuterHTML(child, node, callback);
}
return s;
};
}
// make cloning/importing work!
if (needsTemplate || needsCloning) {
PolyfilledHTMLTemplateElement._cloneNode = function _cloneNode(template, deep) {
var clone = capturedCloneNode.call(template, false);
// NOTE: decorate doesn't auto-fix children because they are already
// decorated so they need special clone fixup.
if (this.decorate) {
this.decorate(clone);
}
if (deep) {
// NOTE: use native clone node to make sure CE's wrapped
// cloneNode does not cause elements to upgrade.
capturedAppendChild.call(clone.content, capturedCloneNode.call(template.content, true));
// now ensure nested templates are cloned correctly.
fixClonedDom(clone.content, template.content);
}
return clone;
};
// Given a source and cloned subtree, find <template>'s in the cloned
// subtree and replace them with cloned <template>'s from source.
// We must do this because only the source templates have proper .content.
var fixClonedDom = function fixClonedDom(clone, source) {
// do nothing if cloned node is not an element
if (!source.querySelectorAll) return;
// these two lists should be coincident
var s$ = QSA(source, TEMPLATE_TAG);
if (s$.length === 0) {
return;
}
var t$ = QSA(clone, TEMPLATE_TAG);
for (var i=0, l=t$.length, t, s; i<l; i++) {
s = s$[i];
t = t$[i];
if (PolyfilledHTMLTemplateElement && PolyfilledHTMLTemplateElement.decorate) {
PolyfilledHTMLTemplateElement.decorate(s);
}
capturedReplaceChild.call(t.parentNode, cloneNode.call(s, true), t);
}
};
// make sure scripts inside of a cloned template are executable
var fixClonedScripts = function fixClonedScripts(fragment) {
var scripts = QSA(fragment, scriptSelector);
for (var ns, s, i = 0; i < scripts.length; i++) {
s = scripts[i];
ns = capturedCreateElement.call(document, 'script');
ns.textContent = s.textContent;
var attrs = s.attributes;
for (var ai = 0, a; ai < attrs.length; ai++) {
a = attrs[ai];
ns.setAttribute(a.name, a.value);
}
capturedReplaceChild.call(s.parentNode, ns, s);
}
};
// override all cloning to fix the cloned subtree to contain properly
// cloned templates.
var cloneNode = Node.prototype.cloneNode = function cloneNode(deep) {
var dom;
// workaround for Edge bug cloning documentFragments
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8619646/
if (!needsDocFrag && brokenDocFragment && this instanceof DocumentFragment) {
if (!deep) {
return this.ownerDocument.createDocumentFragment();
} else {
dom = importNode.call(this.ownerDocument, this, true);
}
} else if (this.nodeType === Node.ELEMENT_NODE &&
this.localName === TEMPLATE_TAG &&
this.namespaceURI == document.documentElement.namespaceURI) {
dom = PolyfilledHTMLTemplateElement._cloneNode(this, deep);
} else {
dom = capturedCloneNode.call(this, deep);
}
// template.content is cloned iff `deep`.
if (deep) {
fixClonedDom(dom, this);
}
return dom;
};
// NOTE: we are cloning instead of importing <template>'s.
// However, the ownerDocument of the cloned template will be correct!
// This is because the native import node creates the right document owned
// subtree and `fixClonedDom` inserts cloned templates into this subtree,
// thus updating the owner doc.
var importNode = Document.prototype.importNode = function importNode(element, deep) {
deep = deep || false;
if (element.localName === TEMPLATE_TAG) {
return PolyfilledHTMLTemplateElement._cloneNode(element, deep);
} else {
var dom = capturedImportNode.call(this, element, deep);
if (deep) {
fixClonedDom(dom, element);
fixClonedScripts(dom);
}
return dom;
}
};
}
if (needsTemplate) {
window.HTMLTemplateElement = PolyfilledHTMLTemplateElement;
}
})();

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