mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 11:14:38 +08:00
Merge branch 'master' into coolreader18/demo-repl-continuation
This commit is contained in:
215 files changed
+26425
-7564
No files matched your search
@@ -9,3 +9,4 @@ __pycache__
|
||||
.vscode
|
||||
wasm-pack.log
|
||||
.idea/
|
||||
tests/snippets/resources
|
||||
Generated
+641
-416
File diff suppressed because it is too large.
Load diff
+9
-1
@@ -7,11 +7,19 @@ edition = "2018"
|
||||
[workspace]
|
||||
members = [".", "derive", "vm", "wasm/lib", "parser"]
|
||||
|
||||
[[bench]]
|
||||
name = "bench"
|
||||
path = "./benchmarks/bench.rs"
|
||||
|
||||
|
||||
[dependencies]
|
||||
log="0.4.1"
|
||||
env_logger="0.5.10"
|
||||
clap = "2.31.2"
|
||||
rustpython_parser = {path = "parser"}
|
||||
rustpython_vm = {path = "vm"}
|
||||
rustyline = "2.1.0"
|
||||
rustyline = "4.1.0"
|
||||
xdg = "2.2.0"
|
||||
|
||||
[dev-dependencies.cpython]
|
||||
version = "0.2"
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
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 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 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.
|
||||
@@ -0,0 +1,10 @@
|
||||
Standard Library for RustPython
|
||||
===============================
|
||||
|
||||
This directory contains all of the Python files that make up the standard library for RustPython.
|
||||
|
||||
Most of these files are copied over from the CPython repository(the 3.7 branch), with slight modifications to allow them
|
||||
to work under RustPython. The current goal is to complete the standard library with as few modifications as possible.
|
||||
Current modifications are just temporary workarounds for bugs/missing feature within the RustPython implementation.
|
||||
|
||||
The first target is to run the ``unittest`` module, so we can leverage the CPython test suite.
|
||||
File diff suppressed because it is too large.
Load diff
+147
@@ -0,0 +1,147 @@
|
||||
from _weakrefset import WeakSet
|
||||
|
||||
|
||||
def get_cache_token():
|
||||
"""Returns the current ABC cache token.
|
||||
|
||||
The token is an opaque object (supporting equality testing) identifying the
|
||||
current version of the ABC cache for virtual subclasses. The token changes
|
||||
with every call to ``register()`` on any ABC.
|
||||
"""
|
||||
return ABCMeta._abc_invalidation_counter
|
||||
|
||||
|
||||
class ABCMeta(type):
|
||||
"""Metaclass for defining Abstract Base Classes (ABCs).
|
||||
|
||||
Use this metaclass to create an ABC. An ABC can be subclassed
|
||||
directly, and then acts as a mix-in class. You can also register
|
||||
unrelated concrete classes (even built-in classes) and unrelated
|
||||
ABCs as 'virtual subclasses' -- these and their descendants will
|
||||
be considered subclasses of the registering ABC by the built-in
|
||||
issubclass() function, but the registering ABC won't show up in
|
||||
their MRO (Method Resolution Order) nor will method
|
||||
implementations defined by the registering ABC be callable (not
|
||||
even via super()).
|
||||
"""
|
||||
|
||||
# A global counter that is incremented each time a class is
|
||||
# registered as a virtual subclass of anything. It forces the
|
||||
# negative cache to be cleared before its next use.
|
||||
# Note: this counter is private. Use `abc.get_cache_token()` for
|
||||
# external code.
|
||||
_abc_invalidation_counter = 0
|
||||
|
||||
def __new__(mcls, name, bases, namespace, **kwargs):
|
||||
cls = type.__new__(mcls, name, bases, namespace, **kwargs)
|
||||
# Compute set of abstract method names
|
||||
abstracts = {name
|
||||
for name, value in namespace.items()
|
||||
if getattr(value, "__isabstractmethod__", False)}
|
||||
for base in bases:
|
||||
for name in getattr(base, "__abstractmethods__", set()):
|
||||
value = getattr(cls, name, None)
|
||||
if getattr(value, "__isabstractmethod__", False):
|
||||
abstracts.add(name)
|
||||
cls.__abstractmethods__ = set(abstracts)
|
||||
# Set up inheritance registry
|
||||
cls._abc_registry = WeakSet()
|
||||
cls._abc_cache = WeakSet()
|
||||
cls._abc_negative_cache = WeakSet()
|
||||
cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
|
||||
return cls
|
||||
|
||||
def register(cls, subclass):
|
||||
"""Register a virtual subclass of an ABC.
|
||||
|
||||
Returns the subclass, to allow usage as a class decorator.
|
||||
"""
|
||||
if not isinstance(subclass, type):
|
||||
raise TypeError("Can only register classes")
|
||||
if issubclass(subclass, cls):
|
||||
return subclass # Already a subclass
|
||||
# Subtle: test for cycles *after* testing for "already a subclass";
|
||||
# this means we allow X.register(X) and interpret it as a no-op.
|
||||
if issubclass(cls, subclass):
|
||||
# This would create a cycle, which is bad for the algorithm below
|
||||
raise RuntimeError("Refusing to create an inheritance cycle")
|
||||
cls._abc_registry.add(subclass)
|
||||
ABCMeta._abc_invalidation_counter += 1 # Invalidate negative cache
|
||||
return subclass
|
||||
|
||||
def _dump_registry(cls, file=None):
|
||||
"""Debug helper to print the ABC registry."""
|
||||
print(f"Class: {cls.__module__}.{cls.__qualname__}", file=file)
|
||||
print(f"Inv. counter: {get_cache_token()}", file=file)
|
||||
for name in cls.__dict__:
|
||||
if name.startswith("_abc_"):
|
||||
value = getattr(cls, name)
|
||||
if isinstance(value, WeakSet):
|
||||
value = set(value)
|
||||
print(f"{name}: {value!r}", file=file)
|
||||
|
||||
def _abc_registry_clear(cls):
|
||||
"""Clear the registry (for debugging or testing)."""
|
||||
cls._abc_registry.clear()
|
||||
|
||||
def _abc_caches_clear(cls):
|
||||
"""Clear the caches (for debugging or testing)."""
|
||||
cls._abc_cache.clear()
|
||||
cls._abc_negative_cache.clear()
|
||||
|
||||
def __instancecheck__(cls, instance):
|
||||
"""Override for isinstance(instance, cls)."""
|
||||
# Inline the cache checking
|
||||
subclass = instance.__class__
|
||||
if subclass in cls._abc_cache:
|
||||
return True
|
||||
subtype = type(instance)
|
||||
if subtype is subclass:
|
||||
if (cls._abc_negative_cache_version ==
|
||||
ABCMeta._abc_invalidation_counter and
|
||||
subclass in cls._abc_negative_cache):
|
||||
return False
|
||||
# Fall back to the subclass check.
|
||||
return cls.__subclasscheck__(subclass)
|
||||
return any(cls.__subclasscheck__(c) for c in (subclass, subtype))
|
||||
|
||||
def __subclasscheck__(cls, subclass):
|
||||
"""Override for issubclass(subclass, cls)."""
|
||||
if not isinstance(subclass, type):
|
||||
raise TypeError('issubclass() arg 1 must be a class')
|
||||
# Check cache
|
||||
if subclass in cls._abc_cache:
|
||||
return True
|
||||
# Check negative cache; may have to invalidate
|
||||
if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter:
|
||||
# Invalidate the negative cache
|
||||
cls._abc_negative_cache = WeakSet()
|
||||
cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
|
||||
elif subclass in cls._abc_negative_cache:
|
||||
return False
|
||||
# Check the subclass hook
|
||||
ok = cls.__subclasshook__(subclass)
|
||||
if ok is not NotImplemented:
|
||||
assert isinstance(ok, bool)
|
||||
if ok:
|
||||
cls._abc_cache.add(subclass)
|
||||
else:
|
||||
cls._abc_negative_cache.add(subclass)
|
||||
return ok
|
||||
# Check if it's a direct subclass
|
||||
if cls in getattr(subclass, '__mro__', ()):
|
||||
cls._abc_cache.add(subclass)
|
||||
return True
|
||||
# Check if it's a subclass of a registered class (recursive)
|
||||
for rcls in cls._abc_registry:
|
||||
if issubclass(subclass, rcls):
|
||||
cls._abc_cache.add(subclass)
|
||||
return True
|
||||
# Check if it's a subclass of a subclass (recursive)
|
||||
for scls in cls.__subclasses__():
|
||||
if issubclass(subclass, scls):
|
||||
cls._abc_cache.add(subclass)
|
||||
return True
|
||||
# No dice; update negative cache
|
||||
cls._abc_negative_cache.add(subclass)
|
||||
return False
|
||||
@@ -0,0 +1,196 @@
|
||||
# Access WeakSet through the weakref module.
|
||||
# This code is separated-out because it is needed
|
||||
# by abc.py to load everything else at startup.
|
||||
|
||||
from _weakref import ref
|
||||
|
||||
__all__ = ['WeakSet']
|
||||
|
||||
|
||||
class _IterationGuard:
|
||||
# This context manager registers itself in the current iterators of the
|
||||
# weak container, such as to delay all removals until the context manager
|
||||
# exits.
|
||||
# This technique should be relatively thread-safe (since sets are).
|
||||
|
||||
def __init__(self, weakcontainer):
|
||||
# Don't create cycles
|
||||
self.weakcontainer = ref(weakcontainer)
|
||||
|
||||
def __enter__(self):
|
||||
w = self.weakcontainer()
|
||||
if w is not None:
|
||||
w._iterating.add(self)
|
||||
return self
|
||||
|
||||
def __exit__(self, e, t, b):
|
||||
w = self.weakcontainer()
|
||||
if w is not None:
|
||||
s = w._iterating
|
||||
s.remove(self)
|
||||
if not s:
|
||||
w._commit_removals()
|
||||
|
||||
|
||||
class WeakSet:
|
||||
def __init__(self, data=None):
|
||||
self.data = set()
|
||||
def _remove(item, selfref=ref(self)):
|
||||
self = selfref()
|
||||
if self is not None:
|
||||
if self._iterating:
|
||||
self._pending_removals.append(item)
|
||||
else:
|
||||
self.data.discard(item)
|
||||
self._remove = _remove
|
||||
# A list of keys to be removed
|
||||
self._pending_removals = []
|
||||
self._iterating = set()
|
||||
if data is not None:
|
||||
self.update(data)
|
||||
|
||||
def _commit_removals(self):
|
||||
l = self._pending_removals
|
||||
discard = self.data.discard
|
||||
while l:
|
||||
discard(l.pop())
|
||||
|
||||
def __iter__(self):
|
||||
with _IterationGuard(self):
|
||||
for itemref in self.data:
|
||||
item = itemref()
|
||||
if item is not None:
|
||||
# Caveat: the iterator will keep a strong reference to
|
||||
# `item` until it is resumed or closed.
|
||||
yield item
|
||||
|
||||
def __len__(self):
|
||||
return len(self.data) - len(self._pending_removals)
|
||||
|
||||
def __contains__(self, item):
|
||||
try:
|
||||
wr = ref(item)
|
||||
except TypeError:
|
||||
return False
|
||||
return wr in self.data
|
||||
|
||||
def __reduce__(self):
|
||||
return (self.__class__, (list(self),),
|
||||
getattr(self, '__dict__', None))
|
||||
|
||||
def add(self, item):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
self.data.add(ref(item, self._remove))
|
||||
|
||||
def clear(self):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
self.data.clear()
|
||||
|
||||
def copy(self):
|
||||
return self.__class__(self)
|
||||
|
||||
def pop(self):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
while True:
|
||||
try:
|
||||
itemref = self.data.pop()
|
||||
except KeyError:
|
||||
raise KeyError('pop from empty WeakSet') from None
|
||||
item = itemref()
|
||||
if item is not None:
|
||||
return item
|
||||
|
||||
def remove(self, item):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
self.data.remove(ref(item))
|
||||
|
||||
def discard(self, item):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
self.data.discard(ref(item))
|
||||
|
||||
def update(self, other):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
for element in other:
|
||||
self.add(element)
|
||||
|
||||
def __ior__(self, other):
|
||||
self.update(other)
|
||||
return self
|
||||
|
||||
def difference(self, other):
|
||||
newset = self.copy()
|
||||
newset.difference_update(other)
|
||||
return newset
|
||||
__sub__ = difference
|
||||
|
||||
def difference_update(self, other):
|
||||
self.__isub__(other)
|
||||
def __isub__(self, other):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
if self is other:
|
||||
self.data.clear()
|
||||
else:
|
||||
self.data.difference_update(ref(item) for item in other)
|
||||
return self
|
||||
|
||||
def intersection(self, other):
|
||||
return self.__class__(item for item in other if item in self)
|
||||
__and__ = intersection
|
||||
|
||||
def intersection_update(self, other):
|
||||
self.__iand__(other)
|
||||
def __iand__(self, other):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
self.data.intersection_update(ref(item) for item in other)
|
||||
return self
|
||||
|
||||
def issubset(self, other):
|
||||
return self.data.issubset(ref(item) for item in other)
|
||||
__le__ = issubset
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.data < set(map(ref, other))
|
||||
|
||||
def issuperset(self, other):
|
||||
return self.data.issuperset(ref(item) for item in other)
|
||||
__ge__ = issuperset
|
||||
|
||||
def __gt__(self, other):
|
||||
return self.data > set(map(ref, other))
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return NotImplemented
|
||||
return self.data == set(map(ref, other))
|
||||
|
||||
def symmetric_difference(self, other):
|
||||
newset = self.copy()
|
||||
newset.symmetric_difference_update(other)
|
||||
return newset
|
||||
__xor__ = symmetric_difference
|
||||
|
||||
def symmetric_difference_update(self, other):
|
||||
self.__ixor__(other)
|
||||
def __ixor__(self, other):
|
||||
if self._pending_removals:
|
||||
self._commit_removals()
|
||||
if self is other:
|
||||
self.data.clear()
|
||||
else:
|
||||
self.data.symmetric_difference_update(ref(item, self._remove) for item in other)
|
||||
return self
|
||||
|
||||
def union(self, other):
|
||||
return self.__class__(e for s in (self, other) for e in s)
|
||||
__or__ = union
|
||||
|
||||
def isdisjoint(self, other):
|
||||
return len(self.intersection(other)) == 0
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
# Copyright 2007 Google, Inc. All Rights Reserved.
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
"""Abstract Base Classes (ABCs) according to PEP 3119."""
|
||||
|
||||
|
||||
def abstractmethod(funcobj):
|
||||
"""A decorator indicating abstract methods.
|
||||
|
||||
Requires that the metaclass is ABCMeta or derived from it. A
|
||||
class that has a metaclass derived from ABCMeta cannot be
|
||||
instantiated unless all of its abstract methods are overridden.
|
||||
The abstract methods can be called using any of the normal
|
||||
'super' call mechanisms.
|
||||
|
||||
Usage:
|
||||
|
||||
class C(metaclass=ABCMeta):
|
||||
@abstractmethod
|
||||
def my_abstract_method(self, ...):
|
||||
...
|
||||
"""
|
||||
funcobj.__isabstractmethod__ = True
|
||||
return funcobj
|
||||
|
||||
|
||||
class abstractclassmethod(classmethod):
|
||||
"""A decorator indicating abstract classmethods.
|
||||
|
||||
Similar to abstractmethod.
|
||||
|
||||
Usage:
|
||||
|
||||
class C(metaclass=ABCMeta):
|
||||
@abstractclassmethod
|
||||
def my_abstract_classmethod(cls, ...):
|
||||
...
|
||||
|
||||
'abstractclassmethod' is deprecated. Use 'classmethod' with
|
||||
'abstractmethod' instead.
|
||||
"""
|
||||
|
||||
__isabstractmethod__ = True
|
||||
|
||||
def __init__(self, callable):
|
||||
callable.__isabstractmethod__ = True
|
||||
super().__init__(callable)
|
||||
|
||||
|
||||
class abstractstaticmethod(staticmethod):
|
||||
"""A decorator indicating abstract staticmethods.
|
||||
|
||||
Similar to abstractmethod.
|
||||
|
||||
Usage:
|
||||
|
||||
class C(metaclass=ABCMeta):
|
||||
@abstractstaticmethod
|
||||
def my_abstract_staticmethod(...):
|
||||
...
|
||||
|
||||
'abstractstaticmethod' is deprecated. Use 'staticmethod' with
|
||||
'abstractmethod' instead.
|
||||
"""
|
||||
|
||||
__isabstractmethod__ = True
|
||||
|
||||
def __init__(self, callable):
|
||||
callable.__isabstractmethod__ = True
|
||||
super().__init__(callable)
|
||||
|
||||
|
||||
class abstractproperty(property):
|
||||
"""A decorator indicating abstract properties.
|
||||
|
||||
Requires that the metaclass is ABCMeta or derived from it. A
|
||||
class that has a metaclass derived from ABCMeta cannot be
|
||||
instantiated unless all of its abstract properties are overridden.
|
||||
The abstract properties can be called using any of the normal
|
||||
'super' call mechanisms.
|
||||
|
||||
Usage:
|
||||
|
||||
class C(metaclass=ABCMeta):
|
||||
@abstractproperty
|
||||
def my_abstract_property(self):
|
||||
...
|
||||
|
||||
This defines a read-only property; you can also define a read-write
|
||||
abstract property using the 'long' form of property declaration:
|
||||
|
||||
class C(metaclass=ABCMeta):
|
||||
def getx(self): ...
|
||||
def setx(self, value): ...
|
||||
x = abstractproperty(getx, setx)
|
||||
|
||||
'abstractproperty' is deprecated. Use 'property' with 'abstractmethod'
|
||||
instead.
|
||||
"""
|
||||
|
||||
__isabstractmethod__ = True
|
||||
|
||||
|
||||
try:
|
||||
from _abc import (get_cache_token, _abc_init, _abc_register,
|
||||
_abc_instancecheck, _abc_subclasscheck, _get_dump,
|
||||
_reset_registry, _reset_caches)
|
||||
except ImportError:
|
||||
from _py_abc import ABCMeta, get_cache_token
|
||||
ABCMeta.__module__ = 'abc'
|
||||
else:
|
||||
class ABCMeta(type):
|
||||
"""Metaclass for defining Abstract Base Classes (ABCs).
|
||||
|
||||
Use this metaclass to create an ABC. An ABC can be subclassed
|
||||
directly, and then acts as a mix-in class. You can also register
|
||||
unrelated concrete classes (even built-in classes) and unrelated
|
||||
ABCs as 'virtual subclasses' -- these and their descendants will
|
||||
be considered subclasses of the registering ABC by the built-in
|
||||
issubclass() function, but the registering ABC won't show up in
|
||||
their MRO (Method Resolution Order) nor will method
|
||||
implementations defined by the registering ABC be callable (not
|
||||
even via super()).
|
||||
"""
|
||||
def __new__(mcls, name, bases, namespace, **kwargs):
|
||||
cls = super().__new__(mcls, name, bases, namespace, **kwargs)
|
||||
_abc_init(cls)
|
||||
return cls
|
||||
|
||||
def register(cls, subclass):
|
||||
"""Register a virtual subclass of an ABC.
|
||||
|
||||
Returns the subclass, to allow usage as a class decorator.
|
||||
"""
|
||||
return _abc_register(cls, subclass)
|
||||
|
||||
def __instancecheck__(cls, instance):
|
||||
"""Override for isinstance(instance, cls)."""
|
||||
return _abc_instancecheck(cls, instance)
|
||||
|
||||
def __subclasscheck__(cls, subclass):
|
||||
"""Override for issubclass(subclass, cls)."""
|
||||
return _abc_subclasscheck(cls, subclass)
|
||||
|
||||
def _dump_registry(cls, file=None):
|
||||
"""Debug helper to print the ABC registry."""
|
||||
print(f"Class: {cls.__module__}.{cls.__qualname__}", file=file)
|
||||
print(f"Inv. counter: {get_cache_token()}", file=file)
|
||||
(_abc_registry, _abc_cache, _abc_negative_cache,
|
||||
_abc_negative_cache_version) = _get_dump(cls)
|
||||
print(f"_abc_registry: {_abc_registry!r}", file=file)
|
||||
print(f"_abc_cache: {_abc_cache!r}", file=file)
|
||||
print(f"_abc_negative_cache: {_abc_negative_cache!r}", file=file)
|
||||
print(f"_abc_negative_cache_version: {_abc_negative_cache_version!r}",
|
||||
file=file)
|
||||
|
||||
def _abc_registry_clear(cls):
|
||||
"""Clear the registry (for debugging or testing)."""
|
||||
_reset_registry(cls)
|
||||
|
||||
def _abc_caches_clear(cls):
|
||||
"""Clear the caches (for debugging or testing)."""
|
||||
_reset_caches(cls)
|
||||
|
||||
|
||||
class ABC(metaclass=ABCMeta):
|
||||
"""Helper class that provides a standard way to create an ABC using
|
||||
inheritance.
|
||||
"""
|
||||
__slots__ = ()
|
||||
@@ -0,0 +1,151 @@
|
||||
"""
|
||||
Path operations common to more than one OS
|
||||
Do not use directly. The OS specific modules import the appropriate
|
||||
functions from this module themselves.
|
||||
"""
|
||||
import os
|
||||
import stat
|
||||
|
||||
__all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime',
|
||||
'getsize', 'isdir', 'isfile', 'samefile', 'sameopenfile',
|
||||
'samestat']
|
||||
|
||||
|
||||
# Does a path exist?
|
||||
# This is false for dangling symbolic links on systems that support them.
|
||||
def exists(path):
|
||||
"""Test whether a path exists. Returns False for broken symbolic links"""
|
||||
try:
|
||||
os.stat(path)
|
||||
except (OSError, ValueError):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
# This follows symbolic links, so both islink() and isdir() can be true
|
||||
# for the same path on systems that support symlinks
|
||||
def isfile(path):
|
||||
"""Test whether a path is a regular file"""
|
||||
try:
|
||||
st = os.stat(path)
|
||||
except (OSError, ValueError):
|
||||
return False
|
||||
return stat.S_ISREG(st.st_mode)
|
||||
|
||||
|
||||
# Is a path a directory?
|
||||
# This follows symbolic links, so both islink() and isdir()
|
||||
# can be true for the same path on systems that support symlinks
|
||||
def isdir(s):
|
||||
"""Return true if the pathname refers to an existing directory."""
|
||||
try:
|
||||
st = os.stat(s)
|
||||
except (OSError, ValueError):
|
||||
return False
|
||||
return stat.S_ISDIR(st.st_mode)
|
||||
|
||||
|
||||
def getsize(filename):
|
||||
"""Return the size of a file, reported by os.stat()."""
|
||||
return os.stat(filename).st_size
|
||||
|
||||
|
||||
def getmtime(filename):
|
||||
"""Return the last modification time of a file, reported by os.stat()."""
|
||||
return os.stat(filename).st_mtime
|
||||
|
||||
|
||||
def getatime(filename):
|
||||
"""Return the last access time of a file, reported by os.stat()."""
|
||||
return os.stat(filename).st_atime
|
||||
|
||||
|
||||
def getctime(filename):
|
||||
"""Return the metadata change time of a file, reported by os.stat()."""
|
||||
return os.stat(filename).st_ctime
|
||||
|
||||
|
||||
# Return the longest prefix of all list elements.
|
||||
def commonprefix(m):
|
||||
"Given a list of pathnames, returns the longest common leading component"
|
||||
if not m: return ''
|
||||
# Some people pass in a list of pathname parts to operate in an OS-agnostic
|
||||
# fashion; don't try to translate in that case as that's an abuse of the
|
||||
# API and they are already doing what they need to be OS-agnostic and so
|
||||
# they most likely won't be using an os.PathLike object in the sublists.
|
||||
if not isinstance(m[0], (list, tuple)):
|
||||
m = tuple(map(os.fspath, m))
|
||||
s1 = min(m)
|
||||
s2 = max(m)
|
||||
for i, c in enumerate(s1):
|
||||
if c != s2[i]:
|
||||
return s1[:i]
|
||||
return s1
|
||||
|
||||
# Are two stat buffers (obtained from stat, fstat or lstat)
|
||||
# describing the same file?
|
||||
def samestat(s1, s2):
|
||||
"""Test whether two stat buffers reference the same file"""
|
||||
return (s1.st_ino == s2.st_ino and
|
||||
s1.st_dev == s2.st_dev)
|
||||
|
||||
|
||||
# Are two filenames really pointing to the same file?
|
||||
def samefile(f1, f2):
|
||||
"""Test whether two pathnames reference the same actual file"""
|
||||
s1 = os.stat(f1)
|
||||
s2 = os.stat(f2)
|
||||
return samestat(s1, s2)
|
||||
|
||||
|
||||
# Are two open files really referencing the same file?
|
||||
# (Not necessarily the same file descriptor!)
|
||||
def sameopenfile(fp1, fp2):
|
||||
"""Test whether two open file objects reference the same file"""
|
||||
s1 = os.fstat(fp1)
|
||||
s2 = os.fstat(fp2)
|
||||
return samestat(s1, s2)
|
||||
|
||||
|
||||
# Split a path in root and extension.
|
||||
# The extension is everything starting at the last dot in the last
|
||||
# pathname component; the root is everything before that.
|
||||
# It is always true that root + ext == p.
|
||||
|
||||
# Generic implementation of splitext, to be parametrized with
|
||||
# the separators
|
||||
def _splitext(p, sep, altsep, extsep):
|
||||
"""Split the extension from a pathname.
|
||||
|
||||
Extension is everything from the last dot to the end, ignoring
|
||||
leading dots. Returns "(root, ext)"; ext may be empty."""
|
||||
# NOTE: This code must work for text and bytes strings.
|
||||
|
||||
sepIndex = p.rfind(sep)
|
||||
if altsep:
|
||||
altsepIndex = p.rfind(altsep)
|
||||
sepIndex = max(sepIndex, altsepIndex)
|
||||
|
||||
dotIndex = p.rfind(extsep)
|
||||
if dotIndex > sepIndex:
|
||||
# skip all leading dots
|
||||
filenameIndex = sepIndex + 1
|
||||
while filenameIndex < dotIndex:
|
||||
if p[filenameIndex:filenameIndex+1] != extsep:
|
||||
return p[:dotIndex], p[dotIndex:]
|
||||
filenameIndex += 1
|
||||
|
||||
return p, p[:0]
|
||||
|
||||
def _check_arg_types(funcname, *args):
|
||||
hasstr = hasbytes = False
|
||||
for s in args:
|
||||
if isinstance(s, str):
|
||||
hasstr = True
|
||||
elif isinstance(s, bytes):
|
||||
hasbytes = True
|
||||
else:
|
||||
raise TypeError('%s() argument must be str or bytes, not %r' %
|
||||
(funcname, s.__class__.__name__)) from None
|
||||
if hasstr and hasbytes:
|
||||
raise TypeError("Can't mix strings and bytes in path components") from None
|
||||
+601
@@ -0,0 +1,601 @@
|
||||
"""Heap queue algorithm (a.k.a. priority queue).
|
||||
|
||||
Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for
|
||||
all k, counting elements from 0. For the sake of comparison,
|
||||
non-existing elements are considered to be infinite. The interesting
|
||||
property of a heap is that a[0] is always its smallest element.
|
||||
|
||||
Usage:
|
||||
|
||||
heap = [] # creates an empty heap
|
||||
heappush(heap, item) # pushes a new item on the heap
|
||||
item = heappop(heap) # pops the smallest item from the heap
|
||||
item = heap[0] # smallest item on the heap without popping it
|
||||
heapify(x) # transforms list into a heap, in-place, in linear time
|
||||
item = heapreplace(heap, item) # pops and returns smallest item, and adds
|
||||
# new item; the heap size is unchanged
|
||||
|
||||
Our API differs from textbook heap algorithms as follows:
|
||||
|
||||
- We use 0-based indexing. This makes the relationship between the
|
||||
index for a node and the indexes for its children slightly less
|
||||
obvious, but is more suitable since Python uses 0-based indexing.
|
||||
|
||||
- Our heappop() method returns the smallest item, not the largest.
|
||||
|
||||
These two make it possible to view the heap as a regular Python list
|
||||
without surprises: heap[0] is the smallest item, and heap.sort()
|
||||
maintains the heap invariant!
|
||||
"""
|
||||
|
||||
# Original code by Kevin O'Connor, augmented by Tim Peters and Raymond Hettinger
|
||||
|
||||
__about__ = """Heap queues
|
||||
|
||||
[explanation by François Pinard]
|
||||
|
||||
Heaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for
|
||||
all k, counting elements from 0. For the sake of comparison,
|
||||
non-existing elements are considered to be infinite. The interesting
|
||||
property of a heap is that a[0] is always its smallest element.
|
||||
|
||||
The strange invariant above is meant to be an efficient memory
|
||||
representation for a tournament. The numbers below are `k', not a[k]:
|
||||
|
||||
0
|
||||
|
||||
1 2
|
||||
|
||||
3 4 5 6
|
||||
|
||||
7 8 9 10 11 12 13 14
|
||||
|
||||
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
||||
|
||||
|
||||
In the tree above, each cell `k' is topping `2*k+1' and `2*k+2'. In
|
||||
a usual binary tournament we see in sports, each cell is the winner
|
||||
over the two cells it tops, and we can trace the winner down the tree
|
||||
to see all opponents s/he had. However, in many computer applications
|
||||
of such tournaments, we do not need to trace the history of a winner.
|
||||
To be more memory efficient, when a winner is promoted, we try to
|
||||
replace it by something else at a lower level, and the rule becomes
|
||||
that a cell and the two cells it tops contain three different items,
|
||||
but the top cell "wins" over the two topped cells.
|
||||
|
||||
If this heap invariant is protected at all time, index 0 is clearly
|
||||
the overall winner. The simplest algorithmic way to remove it and
|
||||
find the "next" winner is to move some loser (let's say cell 30 in the
|
||||
diagram above) into the 0 position, and then percolate this new 0 down
|
||||
the tree, exchanging values, until the invariant is re-established.
|
||||
This is clearly logarithmic on the total number of items in the tree.
|
||||
By iterating over all items, you get an O(n ln n) sort.
|
||||
|
||||
A nice feature of this sort is that you can efficiently insert new
|
||||
items while the sort is going on, provided that the inserted items are
|
||||
not "better" than the last 0'th element you extracted. This is
|
||||
especially useful in simulation contexts, where the tree holds all
|
||||
incoming events, and the "win" condition means the smallest scheduled
|
||||
time. When an event schedule other events for execution, they are
|
||||
scheduled into the future, so they can easily go into the heap. So, a
|
||||
heap is a good structure for implementing schedulers (this is what I
|
||||
used for my MIDI sequencer :-).
|
||||
|
||||
Various structures for implementing schedulers have been extensively
|
||||
studied, and heaps are good for this, as they are reasonably speedy,
|
||||
the speed is almost constant, and the worst case is not much different
|
||||
than the average case. However, there are other representations which
|
||||
are more efficient overall, yet the worst cases might be terrible.
|
||||
|
||||
Heaps are also very useful in big disk sorts. You most probably all
|
||||
know that a big sort implies producing "runs" (which are pre-sorted
|
||||
sequences, which size is usually related to the amount of CPU memory),
|
||||
followed by a merging passes for these runs, which merging is often
|
||||
very cleverly organised[1]. It is very important that the initial
|
||||
sort produces the longest runs possible. Tournaments are a good way
|
||||
to that. If, using all the memory available to hold a tournament, you
|
||||
replace and percolate items that happen to fit the current run, you'll
|
||||
produce runs which are twice the size of the memory for random input,
|
||||
and much better for input fuzzily ordered.
|
||||
|
||||
Moreover, if you output the 0'th item on disk and get an input which
|
||||
may not fit in the current tournament (because the value "wins" over
|
||||
the last output value), it cannot fit in the heap, so the size of the
|
||||
heap decreases. The freed memory could be cleverly reused immediately
|
||||
for progressively building a second heap, which grows at exactly the
|
||||
same rate the first heap is melting. When the first heap completely
|
||||
vanishes, you switch heaps and start a new run. Clever and quite
|
||||
effective!
|
||||
|
||||
In a word, heaps are useful memory structures to know. I use them in
|
||||
a few applications, and I think it is good to keep a `heap' module
|
||||
around. :-)
|
||||
|
||||
--------------------
|
||||
[1] The disk balancing algorithms which are current, nowadays, are
|
||||
more annoying than clever, and this is a consequence of the seeking
|
||||
capabilities of the disks. On devices which cannot seek, like big
|
||||
tape drives, the story was quite different, and one had to be very
|
||||
clever to ensure (far in advance) that each tape movement will be the
|
||||
most effective possible (that is, will best participate at
|
||||
"progressing" the merge). Some tapes were even able to read
|
||||
backwards, and this was also used to avoid the rewinding time.
|
||||
Believe me, real good tape sorts were quite spectacular to watch!
|
||||
From all times, sorting has always been a Great Art! :-)
|
||||
"""
|
||||
|
||||
__all__ = ['heappush', 'heappop', 'heapify', 'heapreplace', 'merge',
|
||||
'nlargest', 'nsmallest', 'heappushpop']
|
||||
|
||||
def heappush(heap, item):
|
||||
"""Push item onto heap, maintaining the heap invariant."""
|
||||
heap.append(item)
|
||||
_siftdown(heap, 0, len(heap)-1)
|
||||
|
||||
def heappop(heap):
|
||||
"""Pop the smallest item off the heap, maintaining the heap invariant."""
|
||||
lastelt = heap.pop() # raises appropriate IndexError if heap is empty
|
||||
if heap:
|
||||
returnitem = heap[0]
|
||||
heap[0] = lastelt
|
||||
_siftup(heap, 0)
|
||||
return returnitem
|
||||
return lastelt
|
||||
|
||||
def heapreplace(heap, item):
|
||||
"""Pop and return the current smallest value, and add the new item.
|
||||
|
||||
This is more efficient than heappop() followed by heappush(), and can be
|
||||
more appropriate when using a fixed-size heap. Note that the value
|
||||
returned may be larger than item! That constrains reasonable uses of
|
||||
this routine unless written as part of a conditional replacement:
|
||||
|
||||
if item > heap[0]:
|
||||
item = heapreplace(heap, item)
|
||||
"""
|
||||
returnitem = heap[0] # raises appropriate IndexError if heap is empty
|
||||
heap[0] = item
|
||||
_siftup(heap, 0)
|
||||
return returnitem
|
||||
|
||||
def heappushpop(heap, item):
|
||||
"""Fast version of a heappush followed by a heappop."""
|
||||
if heap and heap[0] < item:
|
||||
item, heap[0] = heap[0], item
|
||||
_siftup(heap, 0)
|
||||
return item
|
||||
|
||||
def heapify(x):
|
||||
"""Transform list into a heap, in-place, in O(len(x)) time."""
|
||||
n = len(x)
|
||||
# Transform bottom-up. The largest index there's any point to looking at
|
||||
# is the largest with a child index in-range, so must have 2*i + 1 < n,
|
||||
# or i < (n-1)/2. If n is even = 2*j, this is (2*j-1)/2 = j-1/2 so
|
||||
# j-1 is the largest, which is n//2 - 1. If n is odd = 2*j+1, this is
|
||||
# (2*j+1-1)/2 = j so j-1 is the largest, and that's again n//2-1.
|
||||
for i in reversed(range(n//2)):
|
||||
_siftup(x, i)
|
||||
|
||||
def _heappop_max(heap):
|
||||
"""Maxheap version of a heappop."""
|
||||
lastelt = heap.pop() # raises appropriate IndexError if heap is empty
|
||||
if heap:
|
||||
returnitem = heap[0]
|
||||
heap[0] = lastelt
|
||||
_siftup_max(heap, 0)
|
||||
return returnitem
|
||||
return lastelt
|
||||
|
||||
def _heapreplace_max(heap, item):
|
||||
"""Maxheap version of a heappop followed by a heappush."""
|
||||
returnitem = heap[0] # raises appropriate IndexError if heap is empty
|
||||
heap[0] = item
|
||||
_siftup_max(heap, 0)
|
||||
return returnitem
|
||||
|
||||
def _heapify_max(x):
|
||||
"""Transform list into a maxheap, in-place, in O(len(x)) time."""
|
||||
n = len(x)
|
||||
for i in reversed(range(n//2)):
|
||||
_siftup_max(x, i)
|
||||
|
||||
# 'heap' is a heap at all indices >= startpos, except possibly for pos. pos
|
||||
# is the index of a leaf with a possibly out-of-order value. Restore the
|
||||
# heap invariant.
|
||||
def _siftdown(heap, startpos, pos):
|
||||
newitem = heap[pos]
|
||||
# Follow the path to the root, moving parents down until finding a place
|
||||
# newitem fits.
|
||||
while pos > startpos:
|
||||
parentpos = (pos - 1) >> 1
|
||||
parent = heap[parentpos]
|
||||
if newitem < parent:
|
||||
heap[pos] = parent
|
||||
pos = parentpos
|
||||
continue
|
||||
break
|
||||
heap[pos] = newitem
|
||||
|
||||
# The child indices of heap index pos are already heaps, and we want to make
|
||||
# a heap at index pos too. We do this by bubbling the smaller child of
|
||||
# pos up (and so on with that child's children, etc) until hitting a leaf,
|
||||
# then using _siftdown to move the oddball originally at index pos into place.
|
||||
#
|
||||
# We *could* break out of the loop as soon as we find a pos where newitem <=
|
||||
# both its children, but turns out that's not a good idea, and despite that
|
||||
# many books write the algorithm that way. During a heap pop, the last array
|
||||
# element is sifted in, and that tends to be large, so that comparing it
|
||||
# against values starting from the root usually doesn't pay (= usually doesn't
|
||||
# get us out of the loop early). See Knuth, Volume 3, where this is
|
||||
# explained and quantified in an exercise.
|
||||
#
|
||||
# Cutting the # of comparisons is important, since these routines have no
|
||||
# way to extract "the priority" from an array element, so that intelligence
|
||||
# is likely to be hiding in custom comparison methods, or in array elements
|
||||
# storing (priority, record) tuples. Comparisons are thus potentially
|
||||
# expensive.
|
||||
#
|
||||
# On random arrays of length 1000, making this change cut the number of
|
||||
# comparisons made by heapify() a little, and those made by exhaustive
|
||||
# heappop() a lot, in accord with theory. Here are typical results from 3
|
||||
# runs (3 just to demonstrate how small the variance is):
|
||||
#
|
||||
# Compares needed by heapify Compares needed by 1000 heappops
|
||||
# -------------------------- --------------------------------
|
||||
# 1837 cut to 1663 14996 cut to 8680
|
||||
# 1855 cut to 1659 14966 cut to 8678
|
||||
# 1847 cut to 1660 15024 cut to 8703
|
||||
#
|
||||
# Building the heap by using heappush() 1000 times instead required
|
||||
# 2198, 2148, and 2219 compares: heapify() is more efficient, when
|
||||
# you can use it.
|
||||
#
|
||||
# The total compares needed by list.sort() on the same lists were 8627,
|
||||
# 8627, and 8632 (this should be compared to the sum of heapify() and
|
||||
# heappop() compares): list.sort() is (unsurprisingly!) more efficient
|
||||
# for sorting.
|
||||
|
||||
def _siftup(heap, pos):
|
||||
endpos = len(heap)
|
||||
startpos = pos
|
||||
newitem = heap[pos]
|
||||
# Bubble up the smaller child until hitting a leaf.
|
||||
childpos = 2*pos + 1 # leftmost child position
|
||||
while childpos < endpos:
|
||||
# Set childpos to index of smaller child.
|
||||
rightpos = childpos + 1
|
||||
if rightpos < endpos and not heap[childpos] < heap[rightpos]:
|
||||
childpos = rightpos
|
||||
# Move the smaller child up.
|
||||
heap[pos] = heap[childpos]
|
||||
pos = childpos
|
||||
childpos = 2*pos + 1
|
||||
# The leaf at pos is empty now. Put newitem there, and bubble it up
|
||||
# to its final resting place (by sifting its parents down).
|
||||
heap[pos] = newitem
|
||||
_siftdown(heap, startpos, pos)
|
||||
|
||||
def _siftdown_max(heap, startpos, pos):
|
||||
'Maxheap variant of _siftdown'
|
||||
newitem = heap[pos]
|
||||
# Follow the path to the root, moving parents down until finding a place
|
||||
# newitem fits.
|
||||
while pos > startpos:
|
||||
parentpos = (pos - 1) >> 1
|
||||
parent = heap[parentpos]
|
||||
if parent < newitem:
|
||||
heap[pos] = parent
|
||||
pos = parentpos
|
||||
continue
|
||||
break
|
||||
heap[pos] = newitem
|
||||
|
||||
def _siftup_max(heap, pos):
|
||||
'Maxheap variant of _siftup'
|
||||
endpos = len(heap)
|
||||
startpos = pos
|
||||
newitem = heap[pos]
|
||||
# Bubble up the larger child until hitting a leaf.
|
||||
childpos = 2*pos + 1 # leftmost child position
|
||||
while childpos < endpos:
|
||||
# Set childpos to index of larger child.
|
||||
rightpos = childpos + 1
|
||||
if rightpos < endpos and not heap[rightpos] < heap[childpos]:
|
||||
childpos = rightpos
|
||||
# Move the larger child up.
|
||||
heap[pos] = heap[childpos]
|
||||
pos = childpos
|
||||
childpos = 2*pos + 1
|
||||
# The leaf at pos is empty now. Put newitem there, and bubble it up
|
||||
# to its final resting place (by sifting its parents down).
|
||||
heap[pos] = newitem
|
||||
_siftdown_max(heap, startpos, pos)
|
||||
|
||||
def merge(*iterables, key=None, reverse=False):
|
||||
'''Merge multiple sorted inputs into a single sorted output.
|
||||
|
||||
Similar to sorted(itertools.chain(*iterables)) but returns a generator,
|
||||
does not pull the data into memory all at once, and assumes that each of
|
||||
the input streams is already sorted (smallest to largest).
|
||||
|
||||
>>> list(merge([1,3,5,7], [0,2,4,8], [5,10,15,20], [], [25]))
|
||||
[0, 1, 2, 3, 4, 5, 5, 7, 8, 10, 15, 20, 25]
|
||||
|
||||
If *key* is not None, applies a key function to each element to determine
|
||||
its sort order.
|
||||
|
||||
>>> list(merge(['dog', 'horse'], ['cat', 'fish', 'kangaroo'], key=len))
|
||||
['dog', 'cat', 'fish', 'horse', 'kangaroo']
|
||||
|
||||
'''
|
||||
|
||||
h = []
|
||||
h_append = h.append
|
||||
|
||||
if reverse:
|
||||
_heapify = _heapify_max
|
||||
_heappop = _heappop_max
|
||||
_heapreplace = _heapreplace_max
|
||||
direction = -1
|
||||
else:
|
||||
_heapify = heapify
|
||||
_heappop = heappop
|
||||
_heapreplace = heapreplace
|
||||
direction = 1
|
||||
|
||||
if key is None:
|
||||
for order, it in enumerate(map(iter, iterables)):
|
||||
try:
|
||||
next = it.__next__
|
||||
h_append([next(), order * direction, next])
|
||||
except StopIteration:
|
||||
pass
|
||||
_heapify(h)
|
||||
while len(h) > 1:
|
||||
try:
|
||||
while True:
|
||||
value, order, next = s = h[0]
|
||||
yield value
|
||||
s[0] = next() # raises StopIteration when exhausted
|
||||
_heapreplace(h, s) # restore heap condition
|
||||
except StopIteration:
|
||||
_heappop(h) # remove empty iterator
|
||||
if h:
|
||||
# fast case when only a single iterator remains
|
||||
value, order, next = h[0]
|
||||
yield value
|
||||
yield from next.__self__
|
||||
return
|
||||
|
||||
for order, it in enumerate(map(iter, iterables)):
|
||||
try:
|
||||
next = it.__next__
|
||||
value = next()
|
||||
h_append([key(value), order * direction, value, next])
|
||||
except StopIteration:
|
||||
pass
|
||||
_heapify(h)
|
||||
while len(h) > 1:
|
||||
try:
|
||||
while True:
|
||||
key_value, order, value, next = s = h[0]
|
||||
yield value
|
||||
value = next()
|
||||
s[0] = key(value)
|
||||
s[2] = value
|
||||
_heapreplace(h, s)
|
||||
except StopIteration:
|
||||
_heappop(h)
|
||||
if h:
|
||||
key_value, order, value, next = h[0]
|
||||
yield value
|
||||
yield from next.__self__
|
||||
|
||||
|
||||
# Algorithm notes for nlargest() and nsmallest()
|
||||
# ==============================================
|
||||
#
|
||||
# Make a single pass over the data while keeping the k most extreme values
|
||||
# in a heap. Memory consumption is limited to keeping k values in a list.
|
||||
#
|
||||
# Measured performance for random inputs:
|
||||
#
|
||||
# number of comparisons
|
||||
# n inputs k-extreme values (average of 5 trials) % more than min()
|
||||
# ------------- ---------------- --------------------- -----------------
|
||||
# 1,000 100 3,317 231.7%
|
||||
# 10,000 100 14,046 40.5%
|
||||
# 100,000 100 105,749 5.7%
|
||||
# 1,000,000 100 1,007,751 0.8%
|
||||
# 10,000,000 100 10,009,401 0.1%
|
||||
#
|
||||
# Theoretical number of comparisons for k smallest of n random inputs:
|
||||
#
|
||||
# Step Comparisons Action
|
||||
# ---- -------------------------- ---------------------------
|
||||
# 1 1.66 * k heapify the first k-inputs
|
||||
# 2 n - k compare remaining elements to top of heap
|
||||
# 3 k * (1 + lg2(k)) * ln(n/k) replace the topmost value on the heap
|
||||
# 4 k * lg2(k) - (k/2) final sort of the k most extreme values
|
||||
#
|
||||
# Combining and simplifying for a rough estimate gives:
|
||||
#
|
||||
# comparisons = n + k * (log(k, 2) * log(n/k) + log(k, 2) + log(n/k))
|
||||
#
|
||||
# Computing the number of comparisons for step 3:
|
||||
# -----------------------------------------------
|
||||
# * For the i-th new value from the iterable, the probability of being in the
|
||||
# k most extreme values is k/i. For example, the probability of the 101st
|
||||
# value seen being in the 100 most extreme values is 100/101.
|
||||
# * If the value is a new extreme value, the cost of inserting it into the
|
||||
# heap is 1 + log(k, 2).
|
||||
# * The probability times the cost gives:
|
||||
# (k/i) * (1 + log(k, 2))
|
||||
# * Summing across the remaining n-k elements gives:
|
||||
# sum((k/i) * (1 + log(k, 2)) for i in range(k+1, n+1))
|
||||
# * This reduces to:
|
||||
# (H(n) - H(k)) * k * (1 + log(k, 2))
|
||||
# * Where H(n) is the n-th harmonic number estimated by:
|
||||
# gamma = 0.5772156649
|
||||
# H(n) = log(n, e) + gamma + 1 / (2 * n)
|
||||
# http://en.wikipedia.org/wiki/Harmonic_series_(mathematics)#Rate_of_divergence
|
||||
# * Substituting the H(n) formula:
|
||||
# comparisons = k * (1 + log(k, 2)) * (log(n/k, e) + (1/n - 1/k) / 2)
|
||||
#
|
||||
# Worst-case for step 3:
|
||||
# ----------------------
|
||||
# In the worst case, the input data is reversed sorted so that every new element
|
||||
# must be inserted in the heap:
|
||||
#
|
||||
# comparisons = 1.66 * k + log(k, 2) * (n - k)
|
||||
#
|
||||
# Alternative Algorithms
|
||||
# ----------------------
|
||||
# Other algorithms were not used because they:
|
||||
# 1) Took much more auxiliary memory,
|
||||
# 2) Made multiple passes over the data.
|
||||
# 3) Made more comparisons in common cases (small k, large n, semi-random input).
|
||||
# See the more detailed comparison of approach at:
|
||||
# http://code.activestate.com/recipes/577573-compare-algorithms-for-heapqsmallest
|
||||
|
||||
def nsmallest(n, iterable, key=None):
|
||||
"""Find the n smallest elements in a dataset.
|
||||
|
||||
Equivalent to: sorted(iterable, key=key)[:n]
|
||||
"""
|
||||
|
||||
# Short-cut for n==1 is to use min()
|
||||
if n == 1:
|
||||
it = iter(iterable)
|
||||
sentinel = object()
|
||||
result = min(it, default=sentinel, key=key)
|
||||
return [] if result is sentinel else [result]
|
||||
|
||||
# When n>=size, it's faster to use sorted()
|
||||
try:
|
||||
size = len(iterable)
|
||||
except (TypeError, AttributeError):
|
||||
pass
|
||||
else:
|
||||
if n >= size:
|
||||
return sorted(iterable, key=key)[:n]
|
||||
|
||||
# When key is none, use simpler decoration
|
||||
if key is None:
|
||||
it = iter(iterable)
|
||||
# put the range(n) first so that zip() doesn't
|
||||
# consume one too many elements from the iterator
|
||||
result = [(elem, i) for i, elem in zip(range(n), it)]
|
||||
if not result:
|
||||
return result
|
||||
_heapify_max(result)
|
||||
top = result[0][0]
|
||||
order = n
|
||||
_heapreplace = _heapreplace_max
|
||||
for elem in it:
|
||||
if elem < top:
|
||||
_heapreplace(result, (elem, order))
|
||||
top, _order = result[0]
|
||||
order += 1
|
||||
result.sort()
|
||||
return [elem for (elem, order) in result]
|
||||
|
||||
# General case, slowest method
|
||||
it = iter(iterable)
|
||||
result = [(key(elem), i, elem) for i, elem in zip(range(n), it)]
|
||||
if not result:
|
||||
return result
|
||||
_heapify_max(result)
|
||||
top = result[0][0]
|
||||
order = n
|
||||
_heapreplace = _heapreplace_max
|
||||
for elem in it:
|
||||
k = key(elem)
|
||||
if k < top:
|
||||
_heapreplace(result, (k, order, elem))
|
||||
top, _order, _elem = result[0]
|
||||
order += 1
|
||||
result.sort()
|
||||
return [elem for (k, order, elem) in result]
|
||||
|
||||
def nlargest(n, iterable, key=None):
|
||||
"""Find the n largest elements in a dataset.
|
||||
|
||||
Equivalent to: sorted(iterable, key=key, reverse=True)[:n]
|
||||
"""
|
||||
|
||||
# Short-cut for n==1 is to use max()
|
||||
if n == 1:
|
||||
it = iter(iterable)
|
||||
sentinel = object()
|
||||
result = max(it, default=sentinel, key=key)
|
||||
return [] if result is sentinel else [result]
|
||||
|
||||
# When n>=size, it's faster to use sorted()
|
||||
try:
|
||||
size = len(iterable)
|
||||
except (TypeError, AttributeError):
|
||||
pass
|
||||
else:
|
||||
if n >= size:
|
||||
return sorted(iterable, key=key, reverse=True)[:n]
|
||||
|
||||
# When key is none, use simpler decoration
|
||||
if key is None:
|
||||
it = iter(iterable)
|
||||
result = [(elem, i) for i, elem in zip(range(0, -n, -1), it)]
|
||||
if not result:
|
||||
return result
|
||||
heapify(result)
|
||||
top = result[0][0]
|
||||
order = -n
|
||||
_heapreplace = heapreplace
|
||||
for elem in it:
|
||||
if top < elem:
|
||||
_heapreplace(result, (elem, order))
|
||||
top, _order = result[0]
|
||||
order -= 1
|
||||
result.sort(reverse=True)
|
||||
return [elem for (elem, order) in result]
|
||||
|
||||
# General case, slowest method
|
||||
it = iter(iterable)
|
||||
result = [(key(elem), i, elem) for i, elem in zip(range(0, -n, -1), it)]
|
||||
if not result:
|
||||
return result
|
||||
heapify(result)
|
||||
top = result[0][0]
|
||||
order = -n
|
||||
_heapreplace = heapreplace
|
||||
for elem in it:
|
||||
k = key(elem)
|
||||
if top < k:
|
||||
_heapreplace(result, (k, order, elem))
|
||||
top, _order, _elem = result[0]
|
||||
order -= 1
|
||||
result.sort(reverse=True)
|
||||
return [elem for (k, order, elem) in result]
|
||||
|
||||
# If available, use C implementation
|
||||
try:
|
||||
from _heapq import *
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from _heapq import _heapreplace_max
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from _heapq import _heapify_max
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
from _heapq import _heappop_max
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
import doctest
|
||||
print(doctest.testmod())
|
||||
+661
@@ -0,0 +1,661 @@
|
||||
# Module 'ntpath' -- common operations on WinNT/Win95 pathnames
|
||||
"""Common pathname manipulations, WindowsNT/95 version.
|
||||
|
||||
Instead of importing this module directly, import os and refer to this
|
||||
module as os.path.
|
||||
"""
|
||||
|
||||
# strings representing various path-related bits and pieces
|
||||
# These are primarily for export; internally, they are hardcoded.
|
||||
# Should be set before imports for resolving cyclic dependency.
|
||||
curdir = '.'
|
||||
pardir = '..'
|
||||
extsep = '.'
|
||||
sep = '\\'
|
||||
pathsep = ';'
|
||||
altsep = '/'
|
||||
defpath = '.;C:\\bin'
|
||||
devnull = 'nul'
|
||||
|
||||
import os
|
||||
import sys
|
||||
import stat
|
||||
import genericpath
|
||||
from genericpath import *
|
||||
|
||||
__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
|
||||
"basename","dirname","commonprefix","getsize","getmtime",
|
||||
"getatime","getctime", "islink","exists","lexists","isdir","isfile",
|
||||
"ismount", "expanduser","expandvars","normpath","abspath",
|
||||
"curdir","pardir","sep","pathsep","defpath","altsep",
|
||||
"extsep","devnull","realpath","supports_unicode_filenames","relpath",
|
||||
"samefile", "sameopenfile", "samestat", "commonpath"]
|
||||
|
||||
def _get_bothseps(path):
|
||||
if isinstance(path, bytes):
|
||||
return b'\\/'
|
||||
else:
|
||||
return '\\/'
|
||||
|
||||
# Normalize the case of a pathname and map slashes to backslashes.
|
||||
# Other normalizations (such as optimizing '../' away) are not done
|
||||
# (this is done by normpath).
|
||||
|
||||
def normcase(s):
|
||||
"""Normalize case of pathname.
|
||||
|
||||
Makes all characters lowercase and all slashes into backslashes."""
|
||||
s = os.fspath(s)
|
||||
if isinstance(s, bytes):
|
||||
return s.replace(b'/', b'\\').lower()
|
||||
else:
|
||||
return s.replace('/', '\\').lower()
|
||||
|
||||
|
||||
# Return whether a path is absolute.
|
||||
# Trivial in Posix, harder on Windows.
|
||||
# For Windows it is absolute if it starts with a slash or backslash (current
|
||||
# volume), or if a pathname after the volume-letter-and-colon or UNC-resource
|
||||
# starts with a slash or backslash.
|
||||
|
||||
def isabs(s):
|
||||
"""Test whether a path is absolute"""
|
||||
s = os.fspath(s)
|
||||
s = splitdrive(s)[1]
|
||||
return len(s) > 0 and s[0] in _get_bothseps(s)
|
||||
|
||||
|
||||
# Join two (or more) paths.
|
||||
def join(path, *paths):
|
||||
path = os.fspath(path)
|
||||
if isinstance(path, bytes):
|
||||
sep = b'\\'
|
||||
seps = b'\\/'
|
||||
colon = b':'
|
||||
else:
|
||||
sep = '\\'
|
||||
seps = '\\/'
|
||||
colon = ':'
|
||||
try:
|
||||
if not paths:
|
||||
path[:0] + sep #23780: Ensure compatible data type even if p is null.
|
||||
result_drive, result_path = splitdrive(path)
|
||||
for p in map(os.fspath, paths):
|
||||
p_drive, p_path = splitdrive(p)
|
||||
if p_path and p_path[0] in seps:
|
||||
# Second path is absolute
|
||||
if p_drive or not result_drive:
|
||||
result_drive = p_drive
|
||||
result_path = p_path
|
||||
continue
|
||||
elif p_drive and p_drive != result_drive:
|
||||
if p_drive.lower() != result_drive.lower():
|
||||
# Different drives => ignore the first path entirely
|
||||
result_drive = p_drive
|
||||
result_path = p_path
|
||||
continue
|
||||
# Same drive in different case
|
||||
result_drive = p_drive
|
||||
# Second path is relative to the first
|
||||
if result_path and result_path[-1] not in seps:
|
||||
result_path = result_path + sep
|
||||
result_path = result_path + p_path
|
||||
## add separator between UNC and non-absolute path
|
||||
if (result_path and result_path[0] not in seps and
|
||||
result_drive and result_drive[-1:] != colon):
|
||||
return result_drive + sep + result_path
|
||||
return result_drive + result_path
|
||||
except (TypeError, AttributeError, BytesWarning):
|
||||
genericpath._check_arg_types('join', path, *paths)
|
||||
raise
|
||||
|
||||
|
||||
# Split a path in a drive specification (a drive letter followed by a
|
||||
# colon) and the path specification.
|
||||
# It is always true that drivespec + pathspec == p
|
||||
def splitdrive(p):
|
||||
"""Split a pathname into drive/UNC sharepoint and relative path specifiers.
|
||||
Returns a 2-tuple (drive_or_unc, path); either part may be empty.
|
||||
|
||||
If you assign
|
||||
result = splitdrive(p)
|
||||
It is always true that:
|
||||
result[0] + result[1] == p
|
||||
|
||||
If the path contained a drive letter, drive_or_unc will contain everything
|
||||
up to and including the colon. e.g. splitdrive("c:/dir") returns ("c:", "/dir")
|
||||
|
||||
If the path contained a UNC path, the drive_or_unc will contain the host name
|
||||
and share up to but not including the fourth directory separator character.
|
||||
e.g. splitdrive("//host/computer/dir") returns ("//host/computer", "/dir")
|
||||
|
||||
Paths cannot contain both a drive letter and a UNC path.
|
||||
|
||||
"""
|
||||
p = os.fspath(p)
|
||||
if len(p) >= 2:
|
||||
if isinstance(p, bytes):
|
||||
sep = b'\\'
|
||||
altsep = b'/'
|
||||
colon = b':'
|
||||
else:
|
||||
sep = '\\'
|
||||
altsep = '/'
|
||||
colon = ':'
|
||||
normp = p.replace(altsep, sep)
|
||||
if (normp[0:2] == sep*2) and (normp[2:3] != sep):
|
||||
# is a UNC path:
|
||||
# vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
|
||||
# \\machine\mountpoint\directory\etc\...
|
||||
# directory ^^^^^^^^^^^^^^^
|
||||
index = normp.find(sep, 2)
|
||||
if index == -1:
|
||||
return p[:0], p
|
||||
index2 = normp.find(sep, index + 1)
|
||||
# a UNC path can't have two slashes in a row
|
||||
# (after the initial two)
|
||||
if index2 == index + 1:
|
||||
return p[:0], p
|
||||
if index2 == -1:
|
||||
index2 = len(p)
|
||||
return p[:index2], p[index2:]
|
||||
if normp[1:2] == colon:
|
||||
return p[:2], p[2:]
|
||||
return p[:0], p
|
||||
|
||||
|
||||
# Split a path in head (everything up to the last '/') and tail (the
|
||||
# rest). After the trailing '/' is stripped, the invariant
|
||||
# join(head, tail) == p holds.
|
||||
# The resulting head won't end in '/' unless it is the root.
|
||||
|
||||
def split(p):
|
||||
"""Split a pathname.
|
||||
|
||||
Return tuple (head, tail) where tail is everything after the final slash.
|
||||
Either part may be empty."""
|
||||
p = os.fspath(p)
|
||||
seps = _get_bothseps(p)
|
||||
d, p = splitdrive(p)
|
||||
# set i to index beyond p's last slash
|
||||
i = len(p)
|
||||
while i and p[i-1] not in seps:
|
||||
i -= 1
|
||||
head, tail = p[:i], p[i:] # now tail has no slashes
|
||||
# remove trailing slashes from head, unless it's all slashes
|
||||
head = head.rstrip(seps) or head
|
||||
return d + head, tail
|
||||
|
||||
|
||||
# Split a path in root and extension.
|
||||
# The extension is everything starting at the last dot in the last
|
||||
# pathname component; the root is everything before that.
|
||||
# It is always true that root + ext == p.
|
||||
|
||||
def splitext(p):
|
||||
p = os.fspath(p)
|
||||
if isinstance(p, bytes):
|
||||
return genericpath._splitext(p, b'\\', b'/', b'.')
|
||||
else:
|
||||
return genericpath._splitext(p, '\\', '/', '.')
|
||||
splitext.__doc__ = genericpath._splitext.__doc__
|
||||
|
||||
|
||||
# Return the tail (basename) part of a path.
|
||||
|
||||
def basename(p):
|
||||
"""Returns the final component of a pathname"""
|
||||
return split(p)[1]
|
||||
|
||||
|
||||
# Return the head (dirname) part of a path.
|
||||
|
||||
def dirname(p):
|
||||
"""Returns the directory component of a pathname"""
|
||||
return split(p)[0]
|
||||
|
||||
# Is a path a symbolic link?
|
||||
# This will always return false on systems where os.lstat doesn't exist.
|
||||
|
||||
def islink(path):
|
||||
"""Test whether a path is a symbolic link.
|
||||
This will always return false for Windows prior to 6.0.
|
||||
"""
|
||||
try:
|
||||
st = os.lstat(path)
|
||||
except (OSError, ValueError, AttributeError):
|
||||
return False
|
||||
return stat.S_ISLNK(st.st_mode)
|
||||
|
||||
# Being true for dangling symbolic links is also useful.
|
||||
|
||||
def lexists(path):
|
||||
"""Test whether a path exists. Returns True for broken symbolic links"""
|
||||
try:
|
||||
st = os.lstat(path)
|
||||
except (OSError, ValueError):
|
||||
return False
|
||||
return True
|
||||
|
||||
# Is a path a mount point?
|
||||
# Any drive letter root (eg c:\)
|
||||
# Any share UNC (eg \\server\share)
|
||||
# Any volume mounted on a filesystem folder
|
||||
#
|
||||
# No one method detects all three situations. Historically we've lexically
|
||||
# detected drive letter roots and share UNCs. The canonical approach to
|
||||
# detecting mounted volumes (querying the reparse tag) fails for the most
|
||||
# common case: drive letter roots. The alternative which uses GetVolumePathName
|
||||
# fails if the drive letter is the result of a SUBST.
|
||||
try:
|
||||
from nt import _getvolumepathname
|
||||
except ImportError:
|
||||
_getvolumepathname = None
|
||||
def ismount(path):
|
||||
"""Test whether a path is a mount point (a drive root, the root of a
|
||||
share, or a mounted volume)"""
|
||||
path = os.fspath(path)
|
||||
seps = _get_bothseps(path)
|
||||
path = abspath(path)
|
||||
root, rest = splitdrive(path)
|
||||
if root and root[0] in seps:
|
||||
return (not rest) or (rest in seps)
|
||||
if rest in seps:
|
||||
return True
|
||||
|
||||
if _getvolumepathname:
|
||||
return path.rstrip(seps) == _getvolumepathname(path).rstrip(seps)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
# Expand paths beginning with '~' or '~user'.
|
||||
# '~' means $HOME; '~user' means that user's home directory.
|
||||
# If the path doesn't begin with '~', or if the user or $HOME is unknown,
|
||||
# the path is returned unchanged (leaving error reporting to whatever
|
||||
# function is called with the expanded path as argument).
|
||||
# See also module 'glob' for expansion of *, ? and [...] in pathnames.
|
||||
# (A function should also be defined to do full *sh-style environment
|
||||
# variable expansion.)
|
||||
|
||||
def expanduser(path):
|
||||
"""Expand ~ and ~user constructs.
|
||||
|
||||
If user or $HOME is unknown, do nothing."""
|
||||
path = os.fspath(path)
|
||||
if isinstance(path, bytes):
|
||||
tilde = b'~'
|
||||
else:
|
||||
tilde = '~'
|
||||
if not path.startswith(tilde):
|
||||
return path
|
||||
i, n = 1, len(path)
|
||||
while i < n and path[i] not in _get_bothseps(path):
|
||||
i += 1
|
||||
|
||||
if 'USERPROFILE' in os.environ:
|
||||
userhome = os.environ['USERPROFILE']
|
||||
elif not 'HOMEPATH' in os.environ:
|
||||
return path
|
||||
else:
|
||||
try:
|
||||
drive = os.environ['HOMEDRIVE']
|
||||
except KeyError:
|
||||
drive = ''
|
||||
userhome = join(drive, os.environ['HOMEPATH'])
|
||||
|
||||
if isinstance(path, bytes):
|
||||
userhome = os.fsencode(userhome)
|
||||
|
||||
if i != 1: #~user
|
||||
userhome = join(dirname(userhome), path[1:i])
|
||||
|
||||
return userhome + path[i:]
|
||||
|
||||
|
||||
# Expand paths containing shell variable substitutions.
|
||||
# The following rules apply:
|
||||
# - no expansion within single quotes
|
||||
# - '$$' is translated into '$'
|
||||
# - '%%' is translated into '%' if '%%' are not seen in %var1%%var2%
|
||||
# - ${varname} is accepted.
|
||||
# - $varname is accepted.
|
||||
# - %varname% is accepted.
|
||||
# - varnames can be made out of letters, digits and the characters '_-'
|
||||
# (though is not verified in the ${varname} and %varname% cases)
|
||||
# XXX With COMMAND.COM you can use any characters in a variable name,
|
||||
# XXX except '^|<>='.
|
||||
|
||||
def expandvars(path):
|
||||
"""Expand shell variables of the forms $var, ${var} and %var%.
|
||||
|
||||
Unknown variables are left unchanged."""
|
||||
path = os.fspath(path)
|
||||
if isinstance(path, bytes):
|
||||
if b'$' not in path and b'%' not in path:
|
||||
return path
|
||||
import string
|
||||
varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii')
|
||||
quote = b'\''
|
||||
percent = b'%'
|
||||
brace = b'{'
|
||||
rbrace = b'}'
|
||||
dollar = b'$'
|
||||
environ = getattr(os, 'environb', None)
|
||||
else:
|
||||
if '$' not in path and '%' not in path:
|
||||
return path
|
||||
import string
|
||||
varchars = string.ascii_letters + string.digits + '_-'
|
||||
quote = '\''
|
||||
percent = '%'
|
||||
brace = '{'
|
||||
rbrace = '}'
|
||||
dollar = '$'
|
||||
environ = os.environ
|
||||
res = path[:0]
|
||||
index = 0
|
||||
pathlen = len(path)
|
||||
while index < pathlen:
|
||||
c = path[index:index+1]
|
||||
if c == quote: # no expansion within single quotes
|
||||
path = path[index + 1:]
|
||||
pathlen = len(path)
|
||||
try:
|
||||
index = path.index(c)
|
||||
res += c + path[:index + 1]
|
||||
except ValueError:
|
||||
res += c + path
|
||||
index = pathlen - 1
|
||||
elif c == percent: # variable or '%'
|
||||
if path[index + 1:index + 2] == percent:
|
||||
res += c
|
||||
index += 1
|
||||
else:
|
||||
path = path[index+1:]
|
||||
pathlen = len(path)
|
||||
try:
|
||||
index = path.index(percent)
|
||||
except ValueError:
|
||||
res += percent + path
|
||||
index = pathlen - 1
|
||||
else:
|
||||
var = path[:index]
|
||||
try:
|
||||
if environ is None:
|
||||
value = os.fsencode(os.environ[os.fsdecode(var)])
|
||||
else:
|
||||
value = environ[var]
|
||||
except KeyError:
|
||||
value = percent + var + percent
|
||||
res += value
|
||||
elif c == dollar: # variable or '$$'
|
||||
if path[index + 1:index + 2] == dollar:
|
||||
res += c
|
||||
index += 1
|
||||
elif path[index + 1:index + 2] == brace:
|
||||
path = path[index+2:]
|
||||
pathlen = len(path)
|
||||
try:
|
||||
index = path.index(rbrace)
|
||||
except ValueError:
|
||||
res += dollar + brace + path
|
||||
index = pathlen - 1
|
||||
else:
|
||||
var = path[:index]
|
||||
try:
|
||||
if environ is None:
|
||||
value = os.fsencode(os.environ[os.fsdecode(var)])
|
||||
else:
|
||||
value = environ[var]
|
||||
except KeyError:
|
||||
value = dollar + brace + var + rbrace
|
||||
res += value
|
||||
else:
|
||||
var = path[:0]
|
||||
index += 1
|
||||
c = path[index:index + 1]
|
||||
while c and c in varchars:
|
||||
var += c
|
||||
index += 1
|
||||
c = path[index:index + 1]
|
||||
try:
|
||||
if environ is None:
|
||||
value = os.fsencode(os.environ[os.fsdecode(var)])
|
||||
else:
|
||||
value = environ[var]
|
||||
except KeyError:
|
||||
value = dollar + var
|
||||
res += value
|
||||
if c:
|
||||
index -= 1
|
||||
else:
|
||||
res += c
|
||||
index += 1
|
||||
return res
|
||||
|
||||
|
||||
# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
|
||||
# Previously, this function also truncated pathnames to 8+3 format,
|
||||
# but as this module is called "ntpath", that's obviously wrong!
|
||||
|
||||
def normpath(path):
|
||||
"""Normalize path, eliminating double slashes, etc."""
|
||||
path = os.fspath(path)
|
||||
if isinstance(path, bytes):
|
||||
sep = b'\\'
|
||||
altsep = b'/'
|
||||
curdir = b'.'
|
||||
pardir = b'..'
|
||||
special_prefixes = (b'\\\\.\\', b'\\\\?\\')
|
||||
else:
|
||||
sep = '\\'
|
||||
altsep = '/'
|
||||
curdir = '.'
|
||||
pardir = '..'
|
||||
special_prefixes = ('\\\\.\\', '\\\\?\\')
|
||||
if path.startswith(special_prefixes):
|
||||
# in the case of paths with these prefixes:
|
||||
# \\.\ -> device names
|
||||
# \\?\ -> literal paths
|
||||
# do not do any normalization, but return the path unchanged
|
||||
return path
|
||||
path = path.replace(altsep, sep)
|
||||
prefix, path = splitdrive(path)
|
||||
|
||||
# collapse initial backslashes
|
||||
if path.startswith(sep):
|
||||
prefix += sep
|
||||
path = path.lstrip(sep)
|
||||
|
||||
comps = path.split(sep)
|
||||
i = 0
|
||||
while i < len(comps):
|
||||
if not comps[i] or comps[i] == curdir:
|
||||
del comps[i]
|
||||
elif comps[i] == pardir:
|
||||
if i > 0 and comps[i-1] != pardir:
|
||||
del comps[i-1:i+1]
|
||||
i -= 1
|
||||
elif i == 0 and prefix.endswith(sep):
|
||||
del comps[i]
|
||||
else:
|
||||
i += 1
|
||||
else:
|
||||
i += 1
|
||||
# If the path is now empty, substitute '.'
|
||||
if not prefix and not comps:
|
||||
comps.append(curdir)
|
||||
return prefix + sep.join(comps)
|
||||
|
||||
def _abspath_fallback(path):
|
||||
"""Return the absolute version of a path as a fallback function in case
|
||||
`nt._getfullpathname` is not available or raises OSError. See bpo-31047 for
|
||||
more.
|
||||
|
||||
"""
|
||||
|
||||
path = os.fspath(path)
|
||||
if not isabs(path):
|
||||
if isinstance(path, bytes):
|
||||
cwd = os.getcwdb()
|
||||
else:
|
||||
cwd = os.getcwd()
|
||||
path = join(cwd, path)
|
||||
return normpath(path)
|
||||
|
||||
# Return an absolute path.
|
||||
try:
|
||||
from nt import _getfullpathname
|
||||
|
||||
except ImportError: # not running on Windows - mock up something sensible
|
||||
abspath = _abspath_fallback
|
||||
|
||||
else: # use native Windows method on Windows
|
||||
def abspath(path):
|
||||
"""Return the absolute version of a path."""
|
||||
try:
|
||||
return normpath(_getfullpathname(path))
|
||||
except (OSError, ValueError):
|
||||
return _abspath_fallback(path)
|
||||
|
||||
# realpath is a no-op on systems without islink support
|
||||
realpath = abspath
|
||||
# Win9x family and earlier have no Unicode filename support.
|
||||
supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
|
||||
sys.getwindowsversion()[3] >= 2)
|
||||
|
||||
def relpath(path, start=None):
|
||||
"""Return a relative version of a path"""
|
||||
path = os.fspath(path)
|
||||
if isinstance(path, bytes):
|
||||
sep = b'\\'
|
||||
curdir = b'.'
|
||||
pardir = b'..'
|
||||
else:
|
||||
sep = '\\'
|
||||
curdir = '.'
|
||||
pardir = '..'
|
||||
|
||||
if start is None:
|
||||
start = curdir
|
||||
|
||||
if not path:
|
||||
raise ValueError("no path specified")
|
||||
|
||||
start = os.fspath(start)
|
||||
try:
|
||||
start_abs = abspath(normpath(start))
|
||||
path_abs = abspath(normpath(path))
|
||||
start_drive, start_rest = splitdrive(start_abs)
|
||||
path_drive, path_rest = splitdrive(path_abs)
|
||||
if normcase(start_drive) != normcase(path_drive):
|
||||
raise ValueError("path is on mount %r, start on mount %r" % (
|
||||
path_drive, start_drive))
|
||||
|
||||
start_list = [x for x in start_rest.split(sep) if x]
|
||||
path_list = [x for x in path_rest.split(sep) if x]
|
||||
# Work out how much of the filepath is shared by start and path.
|
||||
i = 0
|
||||
for e1, e2 in zip(start_list, path_list):
|
||||
if normcase(e1) != normcase(e2):
|
||||
break
|
||||
i += 1
|
||||
|
||||
rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
|
||||
if not rel_list:
|
||||
return curdir
|
||||
return join(*rel_list)
|
||||
except (TypeError, ValueError, AttributeError, BytesWarning, DeprecationWarning):
|
||||
genericpath._check_arg_types('relpath', path, start)
|
||||
raise
|
||||
|
||||
|
||||
# Return the longest common sub-path of the sequence of paths given as input.
|
||||
# The function is case-insensitive and 'separator-insensitive', i.e. if the
|
||||
# only difference between two paths is the use of '\' versus '/' as separator,
|
||||
# they are deemed to be equal.
|
||||
#
|
||||
# However, the returned path will have the standard '\' separator (even if the
|
||||
# given paths had the alternative '/' separator) and will have the case of the
|
||||
# first path given in the sequence. Additionally, any trailing separator is
|
||||
# stripped from the returned path.
|
||||
|
||||
def commonpath(paths):
|
||||
"""Given a sequence of path names, returns the longest common sub-path."""
|
||||
|
||||
if not paths:
|
||||
raise ValueError('commonpath() arg is an empty sequence')
|
||||
|
||||
paths = tuple(map(os.fspath, paths))
|
||||
if isinstance(paths[0], bytes):
|
||||
sep = b'\\'
|
||||
altsep = b'/'
|
||||
curdir = b'.'
|
||||
else:
|
||||
sep = '\\'
|
||||
altsep = '/'
|
||||
curdir = '.'
|
||||
|
||||
try:
|
||||
drivesplits = [splitdrive(p.replace(altsep, sep).lower()) for p in paths]
|
||||
split_paths = [p.split(sep) for d, p in drivesplits]
|
||||
|
||||
try:
|
||||
isabs, = set(p[:1] == sep for d, p in drivesplits)
|
||||
except ValueError:
|
||||
raise ValueError("Can't mix absolute and relative paths") from None
|
||||
|
||||
# Check that all drive letters or UNC paths match. The check is made only
|
||||
# now otherwise type errors for mixing strings and bytes would not be
|
||||
# caught.
|
||||
if len(set(d for d, p in drivesplits)) != 1:
|
||||
raise ValueError("Paths don't have the same drive")
|
||||
|
||||
drive, path = splitdrive(paths[0].replace(altsep, sep))
|
||||
common = path.split(sep)
|
||||
common = [c for c in common if c and c != curdir]
|
||||
|
||||
split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
|
||||
s1 = min(split_paths)
|
||||
s2 = max(split_paths)
|
||||
for i, c in enumerate(s1):
|
||||
if c != s2[i]:
|
||||
common = common[:i]
|
||||
break
|
||||
else:
|
||||
common = common[:len(s1)]
|
||||
|
||||
prefix = drive + sep if isabs else drive
|
||||
return prefix + sep.join(common)
|
||||
except (TypeError, AttributeError):
|
||||
genericpath._check_arg_types('commonpath', *paths)
|
||||
raise
|
||||
|
||||
|
||||
# determine if two files are in fact the same file
|
||||
try:
|
||||
# GetFinalPathNameByHandle is available starting with Windows 6.0.
|
||||
# Windows XP and non-Windows OS'es will mock _getfinalpathname.
|
||||
if sys.getwindowsversion()[:2] >= (6, 0):
|
||||
from nt import _getfinalpathname
|
||||
else:
|
||||
raise ImportError
|
||||
except (AttributeError, ImportError):
|
||||
# On Windows XP and earlier, two files are the same if their absolute
|
||||
# pathnames are the same.
|
||||
# Non-Windows operating systems fake this method with an XP
|
||||
# approximation.
|
||||
def _getfinalpathname(f):
|
||||
return normcase(abspath(f))
|
||||
|
||||
|
||||
try:
|
||||
# The genericpath.isdir implementation uses os.stat and checks the mode
|
||||
# attribute to tell whether or not the path is a directory.
|
||||
# This is overkill on Windows - just pass the path to GetFileAttributes
|
||||
# and check the attribute from there.
|
||||
from nt import _isdir as isdir
|
||||
except ImportError:
|
||||
# Use genericpath.isdir as imported above.
|
||||
pass
|
||||
+452
-3
@@ -1,15 +1,464 @@
|
||||
"""
|
||||
Operator Interface
|
||||
|
||||
This module exports a set of functions corresponding to the intrinsic
|
||||
operators of Python. For example, operator.add(x, y) is equivalent
|
||||
to the expression x+y. The function names are those used for special
|
||||
methods; variants without leading and trailing '__' are also provided
|
||||
for convenience.
|
||||
|
||||
This is the pure Python implementation of the module.
|
||||
"""
|
||||
|
||||
__all__ = ['abs', 'add', 'and_', 'attrgetter', 'concat', 'contains', 'countOf',
|
||||
'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd', 'iand',
|
||||
'iconcat', 'ifloordiv', 'ilshift', 'imatmul', 'imod', 'imul',
|
||||
'index', 'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift',
|
||||
'is_', 'is_not', 'isub', 'itemgetter', 'itruediv', 'ixor', 'le',
|
||||
'length_hint', 'lshift', 'lt', 'matmul', 'methodcaller', 'mod',
|
||||
'mul', 'ne', 'neg', 'not_', 'or_', 'pos', 'pow', 'rshift',
|
||||
'setitem', 'sub', 'truediv', 'truth', 'xor']
|
||||
|
||||
from builtins import abs as _abs
|
||||
|
||||
|
||||
# Comparison operations:
|
||||
# Comparison Operations *******************************************************#
|
||||
|
||||
def lt(a, b):
|
||||
"Same as a < b."
|
||||
return a < b
|
||||
|
||||
def le(a, b):
|
||||
"Same as a <= b."
|
||||
return a <= b
|
||||
|
||||
def eq(a, b):
|
||||
"Same as a == b."
|
||||
return a == b
|
||||
|
||||
|
||||
def ne(a, b):
|
||||
"Same as a != b."
|
||||
return a != b
|
||||
|
||||
|
||||
def ge(a, b):
|
||||
"Same as a >= b."
|
||||
return a >= b
|
||||
|
||||
def gt(a, b):
|
||||
"Same as a > b."
|
||||
return a > b
|
||||
|
||||
# Logical Operations **********************************************************#
|
||||
|
||||
def not_(a):
|
||||
"Same as not a."
|
||||
return not a
|
||||
|
||||
def truth(a):
|
||||
"Return True if a is true, False otherwise."
|
||||
return True if a else False
|
||||
|
||||
def is_(a, b):
|
||||
"Same as a is b."
|
||||
return a is b
|
||||
|
||||
def is_not(a, b):
|
||||
"Same as a is not b."
|
||||
return a is not b
|
||||
|
||||
# Mathematical/Bitwise Operations *********************************************#
|
||||
|
||||
def abs(a):
|
||||
"Same as abs(a)."
|
||||
return _abs(a)
|
||||
|
||||
def add(a, b):
|
||||
"Same as a + b."
|
||||
return a + b
|
||||
|
||||
def and_(a, b):
|
||||
"Same as a & b."
|
||||
return a & b
|
||||
|
||||
def floordiv(a, b):
|
||||
"Same as a // b."
|
||||
return a // b
|
||||
|
||||
def index(a):
|
||||
"Same as a.__index__()."
|
||||
return a.__index__()
|
||||
|
||||
def inv(a):
|
||||
"Same as ~a."
|
||||
return ~a
|
||||
invert = inv
|
||||
|
||||
def lshift(a, b):
|
||||
"Same as a << b."
|
||||
return a << b
|
||||
|
||||
def mod(a, b):
|
||||
"Same as a % b."
|
||||
return a % b
|
||||
|
||||
def mul(a, b):
|
||||
"Same as a * b."
|
||||
return a * b
|
||||
|
||||
def matmul(a, b):
|
||||
"Same as a @ b."
|
||||
return a @ b
|
||||
|
||||
def neg(a):
|
||||
"Same as -a."
|
||||
return -a
|
||||
|
||||
def or_(a, b):
|
||||
"Same as a | b."
|
||||
return a | b
|
||||
|
||||
def pos(a):
|
||||
"Same as +a."
|
||||
return +a
|
||||
|
||||
def pow(a, b):
|
||||
"Same as a ** b."
|
||||
return a ** b
|
||||
|
||||
def rshift(a, b):
|
||||
"Same as a >> b."
|
||||
return a >> b
|
||||
|
||||
def sub(a, b):
|
||||
"Same as a - b."
|
||||
return a - b
|
||||
|
||||
def truediv(a, b):
|
||||
"Same as a / b."
|
||||
return a / b
|
||||
|
||||
def xor(a, b):
|
||||
"Same as a ^ b."
|
||||
return a ^ b
|
||||
|
||||
# Sequence Operations *********************************************************#
|
||||
|
||||
def concat(a, b):
|
||||
"Same as a + b, for a and b sequences."
|
||||
if not hasattr(a, '__getitem__'):
|
||||
msg = "'%s' object can't be concatenated" % type(a).__name__
|
||||
raise TypeError(msg)
|
||||
return a + b
|
||||
|
||||
def contains(a, b):
|
||||
"Same as b in a (note reversed operands)."
|
||||
return b in a
|
||||
|
||||
def countOf(a, b):
|
||||
"Return the number of times b occurs in a."
|
||||
count = 0
|
||||
for i in a:
|
||||
if i == b:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
def delitem(a, b):
|
||||
"Same as del a[b]."
|
||||
del a[b]
|
||||
|
||||
def getitem(a, b):
|
||||
"Same as a[b]."
|
||||
return a[b]
|
||||
|
||||
def indexOf(a, b):
|
||||
"Return the first index of b in a."
|
||||
for i, j in enumerate(a):
|
||||
if j == b:
|
||||
return i
|
||||
else:
|
||||
raise ValueError('sequence.index(x): x not in sequence')
|
||||
|
||||
def setitem(a, b, c):
|
||||
"Same as a[b] = c."
|
||||
a[b] = c
|
||||
|
||||
def length_hint(obj, default=0):
|
||||
"""
|
||||
Return an estimate of the number of items in obj.
|
||||
This is useful for presizing containers when building from an iterable.
|
||||
|
||||
If the object supports len(), the result will be exact. Otherwise, it may
|
||||
over- or under-estimate by an arbitrary amount. The result will be an
|
||||
integer >= 0.
|
||||
"""
|
||||
if not isinstance(default, int):
|
||||
msg = ("'%s' object cannot be interpreted as an integer" %
|
||||
type(default).__name__)
|
||||
raise TypeError(msg)
|
||||
|
||||
try:
|
||||
return len(obj)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
hint = type(obj).__length_hint__
|
||||
except AttributeError:
|
||||
return default
|
||||
|
||||
try:
|
||||
val = hint(obj)
|
||||
except TypeError:
|
||||
return default
|
||||
if val is NotImplemented:
|
||||
return default
|
||||
if not isinstance(val, int):
|
||||
msg = ('__length_hint__ must be integer, not %s' %
|
||||
type(val).__name__)
|
||||
raise TypeError(msg)
|
||||
if val < 0:
|
||||
msg = '__length_hint__() should return >= 0'
|
||||
raise ValueError(msg)
|
||||
return val
|
||||
|
||||
# Generalized Lookup Objects **************************************************#
|
||||
|
||||
class attrgetter:
|
||||
"""
|
||||
Return a callable object that fetches the given attribute(s) from its operand.
|
||||
After f = attrgetter('name'), the call f(r) returns r.name.
|
||||
After g = attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).
|
||||
After h = attrgetter('name.first', 'name.last'), the call h(r) returns
|
||||
(r.name.first, r.name.last).
|
||||
"""
|
||||
__slots__ = ('_attrs', '_call')
|
||||
|
||||
def __init__(self, attr, *attrs):
|
||||
if not attrs:
|
||||
if not isinstance(attr, str):
|
||||
raise TypeError('attribute name must be a string')
|
||||
self._attrs = (attr,)
|
||||
names = attr.split('.')
|
||||
def func(obj):
|
||||
for name in names:
|
||||
obj = getattr(obj, name)
|
||||
return obj
|
||||
self._call = func
|
||||
else:
|
||||
self._attrs = (attr,) + attrs
|
||||
getters = tuple(map(attrgetter, self._attrs))
|
||||
def func(obj):
|
||||
return tuple(getter(obj) for getter in getters)
|
||||
self._call = func
|
||||
|
||||
def __call__(self, obj):
|
||||
return self._call(obj)
|
||||
|
||||
def __repr__(self):
|
||||
return '%s.%s(%s)' % (self.__class__.__module__,
|
||||
self.__class__.__qualname__,
|
||||
', '.join(map(repr, self._attrs)))
|
||||
|
||||
def __reduce__(self):
|
||||
return self.__class__, self._attrs
|
||||
|
||||
class itemgetter:
|
||||
"""
|
||||
Return a callable object that fetches the given item(s) from its operand.
|
||||
After f = itemgetter(2), the call f(r) returns r[2].
|
||||
After g = itemgetter(2, 5, 3), the call g(r) returns (r[2], r[5], r[3])
|
||||
"""
|
||||
__slots__ = ('_items', '_call')
|
||||
|
||||
def __init__(self, item, *items):
|
||||
if not items:
|
||||
self._items = (item,)
|
||||
def func(obj):
|
||||
return obj[item]
|
||||
self._call = func
|
||||
else:
|
||||
self._items = items = (item,) + items
|
||||
def func(obj):
|
||||
return tuple(obj[i] for i in items)
|
||||
self._call = func
|
||||
|
||||
def __call__(self, obj):
|
||||
return self._call(obj)
|
||||
|
||||
def __repr__(self):
|
||||
return '%s.%s(%s)' % (self.__class__.__module__,
|
||||
self.__class__.__name__,
|
||||
', '.join(map(repr, self._items)))
|
||||
|
||||
def __reduce__(self):
|
||||
return self.__class__, self._items
|
||||
|
||||
class methodcaller:
|
||||
"""
|
||||
Return a callable object that calls the given method on its operand.
|
||||
After f = methodcaller('name'), the call f(r) returns r.name().
|
||||
After g = methodcaller('name', 'date', foo=1), the call g(r) returns
|
||||
r.name('date', foo=1).
|
||||
"""
|
||||
__slots__ = ('_name', '_args', '_kwargs')
|
||||
|
||||
def __init__(*args, **kwargs):
|
||||
if len(args) < 2:
|
||||
msg = "methodcaller needs at least one argument, the method name"
|
||||
raise TypeError(msg)
|
||||
self = args[0]
|
||||
self._name = args[1]
|
||||
if not isinstance(self._name, str):
|
||||
raise TypeError('method name must be a string')
|
||||
self._args = args[2:]
|
||||
self._kwargs = kwargs
|
||||
|
||||
def __call__(self, obj):
|
||||
return getattr(obj, self._name)(*self._args, **self._kwargs)
|
||||
|
||||
def __repr__(self):
|
||||
args = [repr(self._name)]
|
||||
args.extend(map(repr, self._args))
|
||||
args.extend('%s=%r' % (k, v) for k, v in self._kwargs.items())
|
||||
return '%s.%s(%s)' % (self.__class__.__module__,
|
||||
self.__class__.__name__,
|
||||
', '.join(args))
|
||||
|
||||
def __reduce__(self):
|
||||
if not self._kwargs:
|
||||
return self.__class__, (self._name,) + self._args
|
||||
else:
|
||||
from functools import partial
|
||||
return partial(self.__class__, self._name, **self._kwargs), self._args
|
||||
|
||||
|
||||
# In-place Operations *********************************************************#
|
||||
|
||||
def iadd(a, b):
|
||||
"Same as a += b."
|
||||
a += b
|
||||
return a
|
||||
|
||||
def iand(a, b):
|
||||
"Same as a &= b."
|
||||
a &= b
|
||||
return a
|
||||
|
||||
def iconcat(a, b):
|
||||
"Same as a += b, for a and b sequences."
|
||||
if not hasattr(a, '__getitem__'):
|
||||
msg = "'%s' object can't be concatenated" % type(a).__name__
|
||||
raise TypeError(msg)
|
||||
a += b
|
||||
return a
|
||||
|
||||
def ifloordiv(a, b):
|
||||
"Same as a //= b."
|
||||
a //= b
|
||||
return a
|
||||
|
||||
def ilshift(a, b):
|
||||
"Same as a <<= b."
|
||||
a <<= b
|
||||
return a
|
||||
|
||||
def imod(a, b):
|
||||
"Same as a %= b."
|
||||
a %= b
|
||||
return a
|
||||
|
||||
def imul(a, b):
|
||||
"Same as a *= b."
|
||||
a *= b
|
||||
return a
|
||||
|
||||
def imatmul(a, b):
|
||||
"Same as a @= b."
|
||||
a @= b
|
||||
return a
|
||||
|
||||
def ior(a, b):
|
||||
"Same as a |= b."
|
||||
a |= b
|
||||
return a
|
||||
|
||||
def ipow(a, b):
|
||||
"Same as a **= b."
|
||||
a **=b
|
||||
return a
|
||||
|
||||
def irshift(a, b):
|
||||
"Same as a >>= b."
|
||||
a >>= b
|
||||
return a
|
||||
|
||||
def isub(a, b):
|
||||
"Same as a -= b."
|
||||
a -= b
|
||||
return a
|
||||
|
||||
def itruediv(a, b):
|
||||
"Same as a /= b."
|
||||
a /= b
|
||||
return a
|
||||
|
||||
def ixor(a, b):
|
||||
"Same as a ^= b."
|
||||
a ^= b
|
||||
return a
|
||||
|
||||
|
||||
try:
|
||||
from _operator import *
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
from _operator import __doc__
|
||||
|
||||
# All of these "__func__ = func" assignments have to happen after importing
|
||||
# from _operator to make sure they're set to the right function
|
||||
__lt__ = lt
|
||||
__le__ = le
|
||||
__eq__ = eq
|
||||
__ne__ = ne
|
||||
__ge__ = ge
|
||||
__gt__ = gt
|
||||
__not__ = not_
|
||||
__abs__ = abs
|
||||
__add__ = add
|
||||
__and__ = and_
|
||||
__floordiv__ = floordiv
|
||||
__index__ = index
|
||||
__inv__ = inv
|
||||
__invert__ = invert
|
||||
__lshift__ = lshift
|
||||
__mod__ = mod
|
||||
__mul__ = mul
|
||||
__matmul__ = matmul
|
||||
__neg__ = neg
|
||||
__or__ = or_
|
||||
__pos__ = pos
|
||||
__pow__ = pow
|
||||
__rshift__ = rshift
|
||||
__sub__ = sub
|
||||
__truediv__ = truediv
|
||||
__xor__ = xor
|
||||
__concat__ = concat
|
||||
__contains__ = contains
|
||||
__delitem__ = delitem
|
||||
__getitem__ = getitem
|
||||
__setitem__ = setitem
|
||||
__iadd__ = iadd
|
||||
__iand__ = iand
|
||||
__iconcat__ = iconcat
|
||||
__ifloordiv__ = ifloordiv
|
||||
__ilshift__ = ilshift
|
||||
__imod__ = imod
|
||||
__imul__ = imul
|
||||
__imatmul__ = imatmul
|
||||
__ior__ = ior
|
||||
__ipow__ = ipow
|
||||
__irshift__ = irshift
|
||||
__isub__ = isub
|
||||
__itruediv__ = itruediv
|
||||
__ixor__ = ixor
|
||||
@@ -0,0 +1,135 @@
|
||||
import sys
|
||||
|
||||
from _os import *
|
||||
|
||||
|
||||
if name == 'nt':
|
||||
linesep = '\r\n'
|
||||
import ntpath as path
|
||||
else:
|
||||
linesep = '\n'
|
||||
import posixpath as path
|
||||
|
||||
|
||||
sys.modules['os.path'] = path
|
||||
from os.path import (curdir, pardir, sep, pathsep, defpath, extsep, altsep,
|
||||
devnull)
|
||||
|
||||
# Change environ to automatically call putenv(), unsetenv if they exist.
|
||||
from _collections_abc import MutableMapping
|
||||
|
||||
class _Environ(MutableMapping):
|
||||
def __init__(self, data, encodekey, decodekey, encodevalue, decodevalue, putenv, unsetenv):
|
||||
self.encodekey = encodekey
|
||||
self.decodekey = decodekey
|
||||
self.encodevalue = encodevalue
|
||||
self.decodevalue = decodevalue
|
||||
self.putenv = putenv
|
||||
self.unsetenv = unsetenv
|
||||
self._data = data
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
value = self._data[self.encodekey(key)]
|
||||
except KeyError:
|
||||
# raise KeyError with the original key value
|
||||
raise KeyError(key) from None
|
||||
|
||||
return self.decodevalue(value)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
key = self.encodekey(key)
|
||||
value = self.encodevalue(value)
|
||||
self.putenv(key, value)
|
||||
self._data[key] = value
|
||||
|
||||
def __delitem__(self, key):
|
||||
encodedkey = self.encodekey(key)
|
||||
self.unsetenv(encodedkey)
|
||||
try:
|
||||
del self._data[encodedkey]
|
||||
except KeyError:
|
||||
# raise KeyError with the original key value
|
||||
raise KeyError(key) from None
|
||||
|
||||
def __iter__(self):
|
||||
# list() from dict object is an atomic operation
|
||||
keys = list(self._data)
|
||||
for key in keys:
|
||||
yield self.decodekey(key)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._data)
|
||||
|
||||
def __repr__(self):
|
||||
return 'environ({{{}}})'.format(', '.join(
|
||||
('{!r}: {!r}'.format(self.decodekey(key), self.decodevalue(value))
|
||||
for key, value in self._data.items())))
|
||||
|
||||
def copy(self):
|
||||
return dict(self)
|
||||
|
||||
def setdefault(self, key, value):
|
||||
if key not in self:
|
||||
self[key] = value
|
||||
return self[key]
|
||||
|
||||
try:
|
||||
_putenv = putenv
|
||||
except NameError:
|
||||
_putenv = lambda key, value: None
|
||||
# else:
|
||||
# if "putenv" not in __all__:
|
||||
# __all__.append("putenv")
|
||||
|
||||
try:
|
||||
_unsetenv = unsetenv
|
||||
except NameError:
|
||||
_unsetenv = lambda key: _putenv(key, "")
|
||||
# else:
|
||||
# if "unsetenv" not in __all__:
|
||||
# __all__.append("unsetenv")
|
||||
|
||||
def _createenviron():
|
||||
# if name == 'nt':
|
||||
# # Where Env Var Names Must Be UPPERCASE
|
||||
# def check_str(value):
|
||||
# if not isinstance(value, str):
|
||||
# raise TypeError("str expected, not %s" % type(value).__name__)
|
||||
# return value
|
||||
# encode = check_str
|
||||
# decode = str
|
||||
# def encodekey(key):
|
||||
# return encode(key).upper()
|
||||
# data = {}
|
||||
# for key, value in environ.items():
|
||||
# data[encodekey(key)] = value
|
||||
# else:
|
||||
# # Where Env Var Names Can Be Mixed Case
|
||||
# encoding = sys.getfilesystemencoding()
|
||||
# def encode(value):
|
||||
# if not isinstance(value, str):
|
||||
# raise TypeError("str expected, not %s" % type(value).__name__)
|
||||
# return value.encode(encoding, 'surrogateescape')
|
||||
# def decode(value):
|
||||
# return value.decode(encoding, 'surrogateescape')
|
||||
# encodekey = encode
|
||||
decode = str
|
||||
encode = str
|
||||
encodekey = encode
|
||||
data = environ
|
||||
return _Environ(data,
|
||||
encodekey, decode,
|
||||
encode, decode,
|
||||
_putenv, _unsetenv)
|
||||
|
||||
# unicode environ
|
||||
environ = _createenviron()
|
||||
del _createenviron
|
||||
|
||||
|
||||
def getenv(key, default=None):
|
||||
"""Get an environment variable, return None if it doesn't exist.
|
||||
The optional second argument can specify an alternate default.
|
||||
key, default and the result are str."""
|
||||
return environ.get(key, default)
|
||||
@@ -0,0 +1,525 @@
|
||||
"""Common operations on Posix pathnames.
|
||||
|
||||
Instead of importing this module directly, import os and refer to
|
||||
this module as os.path. The "os.path" name is an alias for this
|
||||
module on Posix systems; on other systems (e.g. Windows),
|
||||
os.path provides the same operations in a manner specific to that
|
||||
platform, and is an alias to another module (e.g. ntpath).
|
||||
|
||||
Some of this can actually be useful on non-Posix systems too, e.g.
|
||||
for manipulation of the pathname component of URLs.
|
||||
"""
|
||||
|
||||
# Strings representing various path-related bits and pieces.
|
||||
# These are primarily for export; internally, they are hardcoded.
|
||||
# Should be set before imports for resolving cyclic dependency.
|
||||
curdir = '.'
|
||||
pardir = '..'
|
||||
extsep = '.'
|
||||
sep = '/'
|
||||
pathsep = ':'
|
||||
defpath = '/bin:/usr/bin'
|
||||
altsep = None
|
||||
devnull = '/dev/null'
|
||||
|
||||
import os
|
||||
import sys
|
||||
import stat
|
||||
import genericpath
|
||||
from genericpath import *
|
||||
|
||||
__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
|
||||
"basename","dirname","commonprefix","getsize","getmtime",
|
||||
"getatime","getctime","islink","exists","lexists","isdir","isfile",
|
||||
"ismount", "expanduser","expandvars","normpath","abspath",
|
||||
"samefile","sameopenfile","samestat",
|
||||
"curdir","pardir","sep","pathsep","defpath","altsep","extsep",
|
||||
"devnull","realpath","supports_unicode_filenames","relpath",
|
||||
"commonpath"]
|
||||
|
||||
|
||||
def _get_sep(path):
|
||||
if isinstance(path, bytes):
|
||||
return b'/'
|
||||
else:
|
||||
return '/'
|
||||
|
||||
# Normalize the case of a pathname. Trivial in Posix, string.lower on Mac.
|
||||
# On MS-DOS this may also turn slashes into backslashes; however, other
|
||||
# normalizations (such as optimizing '../' away) are not allowed
|
||||
# (another function should be defined to do that).
|
||||
|
||||
def normcase(s):
|
||||
"""Normalize case of pathname. Has no effect under Posix"""
|
||||
return os.fspath(s)
|
||||
|
||||
|
||||
# Return whether a path is absolute.
|
||||
# Trivial in Posix, harder on the Mac or MS-DOS.
|
||||
|
||||
def isabs(s):
|
||||
"""Test whether a path is absolute"""
|
||||
s = os.fspath(s)
|
||||
sep = _get_sep(s)
|
||||
return s.startswith(sep)
|
||||
|
||||
|
||||
# Join pathnames.
|
||||
# Ignore the previous parts if a part is absolute.
|
||||
# Insert a '/' unless the first part is empty or already ends in '/'.
|
||||
|
||||
def join(a, *p):
|
||||
"""Join two or more pathname components, inserting '/' as needed.
|
||||
If any component is an absolute path, all previous path components
|
||||
will be discarded. An empty last part will result in a path that
|
||||
ends with a separator."""
|
||||
a = os.fspath(a)
|
||||
sep = _get_sep(a)
|
||||
path = a
|
||||
try:
|
||||
if not p:
|
||||
path[:0] + sep #23780: Ensure compatible data type even if p is null.
|
||||
for b in map(os.fspath, p):
|
||||
if b.startswith(sep):
|
||||
path = b
|
||||
elif not path or path.endswith(sep):
|
||||
path += b
|
||||
else:
|
||||
path += sep + b
|
||||
except (TypeError, AttributeError, BytesWarning):
|
||||
genericpath._check_arg_types('join', a, *p)
|
||||
raise
|
||||
return path
|
||||
|
||||
|
||||
# Split a path in head (everything up to the last '/') and tail (the
|
||||
# rest). If the path ends in '/', tail will be empty. If there is no
|
||||
# '/' in the path, head will be empty.
|
||||
# Trailing '/'es are stripped from head unless it is the root.
|
||||
|
||||
def split(p):
|
||||
"""Split a pathname. Returns tuple "(head, tail)" where "tail" is
|
||||
everything after the final slash. Either part may be empty."""
|
||||
p = os.fspath(p)
|
||||
sep = _get_sep(p)
|
||||
i = p.rfind(sep) + 1
|
||||
head, tail = p[:i], p[i:]
|
||||
if head and head != sep*len(head):
|
||||
head = head.rstrip(sep)
|
||||
return head, tail
|
||||
|
||||
|
||||
# Split a path in root and extension.
|
||||
# The extension is everything starting at the last dot in the last
|
||||
# pathname component; the root is everything before that.
|
||||
# It is always true that root + ext == p.
|
||||
|
||||
def splitext(p):
|
||||
p = os.fspath(p)
|
||||
if isinstance(p, bytes):
|
||||
sep = b'/'
|
||||
extsep = b'.'
|
||||
else:
|
||||
sep = '/'
|
||||
extsep = '.'
|
||||
return genericpath._splitext(p, sep, None, extsep)
|
||||
splitext.__doc__ = genericpath._splitext.__doc__
|
||||
|
||||
# Split a pathname into a drive specification and the rest of the
|
||||
# path. Useful on DOS/Windows/NT; on Unix, the drive is always empty.
|
||||
|
||||
def splitdrive(p):
|
||||
"""Split a pathname into drive and path. On Posix, drive is always
|
||||
empty."""
|
||||
p = os.fspath(p)
|
||||
return p[:0], p
|
||||
|
||||
|
||||
# Return the tail (basename) part of a path, same as split(path)[1].
|
||||
|
||||
def basename(p):
|
||||
"""Returns the final component of a pathname"""
|
||||
p = os.fspath(p)
|
||||
sep = _get_sep(p)
|
||||
i = p.rfind(sep) + 1
|
||||
return p[i:]
|
||||
|
||||
|
||||
# Return the head (dirname) part of a path, same as split(path)[0].
|
||||
|
||||
def dirname(p):
|
||||
"""Returns the directory component of a pathname"""
|
||||
p = os.fspath(p)
|
||||
sep = _get_sep(p)
|
||||
i = p.rfind(sep) + 1
|
||||
head = p[:i]
|
||||
if head and head != sep*len(head):
|
||||
head = head.rstrip(sep)
|
||||
return head
|
||||
|
||||
|
||||
# Is a path a symbolic link?
|
||||
# This will always return false on systems where os.lstat doesn't exist.
|
||||
|
||||
def islink(path):
|
||||
"""Test whether a path is a symbolic link"""
|
||||
try:
|
||||
st = os.lstat(path)
|
||||
except (OSError, ValueError, AttributeError):
|
||||
return False
|
||||
return stat.S_ISLNK(st.st_mode)
|
||||
|
||||
# Being true for dangling symbolic links is also useful.
|
||||
|
||||
def lexists(path):
|
||||
"""Test whether a path exists. Returns True for broken symbolic links"""
|
||||
try:
|
||||
os.lstat(path)
|
||||
except (OSError, ValueError):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
# Is a path a mount point?
|
||||
# (Does this work for all UNIXes? Is it even guaranteed to work by Posix?)
|
||||
|
||||
def ismount(path):
|
||||
"""Test whether a path is a mount point"""
|
||||
try:
|
||||
s1 = os.lstat(path)
|
||||
except (OSError, ValueError):
|
||||
# It doesn't exist -- so not a mount point. :-)
|
||||
return False
|
||||
else:
|
||||
# A symlink can never be a mount point
|
||||
if stat.S_ISLNK(s1.st_mode):
|
||||
return False
|
||||
|
||||
if isinstance(path, bytes):
|
||||
parent = join(path, b'..')
|
||||
else:
|
||||
parent = join(path, '..')
|
||||
parent = realpath(parent)
|
||||
try:
|
||||
s2 = os.lstat(parent)
|
||||
except (OSError, ValueError):
|
||||
return False
|
||||
|
||||
dev1 = s1.st_dev
|
||||
dev2 = s2.st_dev
|
||||
if dev1 != dev2:
|
||||
return True # path/.. on a different device as path
|
||||
ino1 = s1.st_ino
|
||||
ino2 = s2.st_ino
|
||||
if ino1 == ino2:
|
||||
return True # path/.. is the same i-node as path
|
||||
return False
|
||||
|
||||
|
||||
# Expand paths beginning with '~' or '~user'.
|
||||
# '~' means $HOME; '~user' means that user's home directory.
|
||||
# If the path doesn't begin with '~', or if the user or $HOME is unknown,
|
||||
# the path is returned unchanged (leaving error reporting to whatever
|
||||
# function is called with the expanded path as argument).
|
||||
# See also module 'glob' for expansion of *, ? and [...] in pathnames.
|
||||
# (A function should also be defined to do full *sh-style environment
|
||||
# variable expansion.)
|
||||
|
||||
def expanduser(path):
|
||||
"""Expand ~ and ~user constructions. If user or $HOME is unknown,
|
||||
do nothing."""
|
||||
path = os.fspath(path)
|
||||
if isinstance(path, bytes):
|
||||
tilde = b'~'
|
||||
else:
|
||||
tilde = '~'
|
||||
if not path.startswith(tilde):
|
||||
return path
|
||||
sep = _get_sep(path)
|
||||
i = path.find(sep, 1)
|
||||
if i < 0:
|
||||
i = len(path)
|
||||
if i == 1:
|
||||
if 'HOME' not in os.environ:
|
||||
import pwd
|
||||
try:
|
||||
userhome = pwd.getpwuid(os.getuid()).pw_dir
|
||||
except KeyError:
|
||||
# bpo-10496: if the current user identifier doesn't exist in the
|
||||
# password database, return the path unchanged
|
||||
return path
|
||||
else:
|
||||
userhome = os.environ['HOME']
|
||||
else:
|
||||
import pwd
|
||||
name = path[1:i]
|
||||
if isinstance(name, bytes):
|
||||
name = str(name, 'ASCII')
|
||||
try:
|
||||
pwent = pwd.getpwnam(name)
|
||||
except KeyError:
|
||||
# bpo-10496: if the user name from the path doesn't exist in the
|
||||
# password database, return the path unchanged
|
||||
return path
|
||||
userhome = pwent.pw_dir
|
||||
if isinstance(path, bytes):
|
||||
userhome = os.fsencode(userhome)
|
||||
root = b'/'
|
||||
else:
|
||||
root = '/'
|
||||
userhome = userhome.rstrip(root)
|
||||
return (userhome + path[i:]) or root
|
||||
|
||||
|
||||
# Expand paths containing shell variable substitutions.
|
||||
# This expands the forms $variable and ${variable} only.
|
||||
# Non-existent variables are left unchanged.
|
||||
|
||||
_varprog = None
|
||||
_varprogb = None
|
||||
|
||||
def expandvars(path):
|
||||
"""Expand shell variables of form $var and ${var}. Unknown variables
|
||||
are left unchanged."""
|
||||
path = os.fspath(path)
|
||||
global _varprog, _varprogb
|
||||
if isinstance(path, bytes):
|
||||
if b'$' not in path:
|
||||
return path
|
||||
if not _varprogb:
|
||||
import re
|
||||
_varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII)
|
||||
search = _varprogb.search
|
||||
start = b'{'
|
||||
end = b'}'
|
||||
environ = getattr(os, 'environb', None)
|
||||
else:
|
||||
if '$' not in path:
|
||||
return path
|
||||
if not _varprog:
|
||||
import re
|
||||
_varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII)
|
||||
search = _varprog.search
|
||||
start = '{'
|
||||
end = '}'
|
||||
environ = os.environ
|
||||
i = 0
|
||||
while True:
|
||||
m = search(path, i)
|
||||
if not m:
|
||||
break
|
||||
i, j = m.span(0)
|
||||
name = m.group(1)
|
||||
if name.startswith(start) and name.endswith(end):
|
||||
name = name[1:-1]
|
||||
try:
|
||||
if environ is None:
|
||||
value = os.fsencode(os.environ[os.fsdecode(name)])
|
||||
else:
|
||||
value = environ[name]
|
||||
except KeyError:
|
||||
i = j
|
||||
else:
|
||||
tail = path[j:]
|
||||
path = path[:i] + value
|
||||
i = len(path)
|
||||
path += tail
|
||||
return path
|
||||
|
||||
|
||||
# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
|
||||
# It should be understood that this may change the meaning of the path
|
||||
# if it contains symbolic links!
|
||||
|
||||
def normpath(path):
|
||||
"""Normalize path, eliminating double slashes, etc."""
|
||||
path = os.fspath(path)
|
||||
if isinstance(path, bytes):
|
||||
sep = b'/'
|
||||
empty = b''
|
||||
dot = b'.'
|
||||
dotdot = b'..'
|
||||
else:
|
||||
sep = '/'
|
||||
empty = ''
|
||||
dot = '.'
|
||||
dotdot = '..'
|
||||
if path == empty:
|
||||
return dot
|
||||
initial_slashes = path.startswith(sep)
|
||||
# POSIX allows one or two initial slashes, but treats three or more
|
||||
# as single slash.
|
||||
if (initial_slashes and
|
||||
path.startswith(sep*2) and not path.startswith(sep*3)):
|
||||
initial_slashes = 2
|
||||
comps = path.split(sep)
|
||||
new_comps = []
|
||||
for comp in comps:
|
||||
if comp in (empty, dot):
|
||||
continue
|
||||
if (comp != dotdot or (not initial_slashes and not new_comps) or
|
||||
(new_comps and new_comps[-1] == dotdot)):
|
||||
new_comps.append(comp)
|
||||
elif new_comps:
|
||||
new_comps.pop()
|
||||
comps = new_comps
|
||||
path = sep.join(comps)
|
||||
if initial_slashes:
|
||||
path = sep*initial_slashes + path
|
||||
return path or dot
|
||||
|
||||
|
||||
def abspath(path):
|
||||
"""Return an absolute path."""
|
||||
path = os.fspath(path)
|
||||
if not isabs(path):
|
||||
if isinstance(path, bytes):
|
||||
cwd = os.getcwdb()
|
||||
else:
|
||||
cwd = os.getcwd()
|
||||
path = join(cwd, path)
|
||||
return normpath(path)
|
||||
|
||||
|
||||
# Return a canonical path (i.e. the absolute location of a file on the
|
||||
# filesystem).
|
||||
|
||||
def realpath(filename):
|
||||
"""Return the canonical path of the specified filename, eliminating any
|
||||
symbolic links encountered in the path."""
|
||||
filename = os.fspath(filename)
|
||||
path, ok = _joinrealpath(filename[:0], filename, {})
|
||||
return abspath(path)
|
||||
|
||||
# Join two paths, normalizing and eliminating any symbolic links
|
||||
# encountered in the second path.
|
||||
def _joinrealpath(path, rest, seen):
|
||||
if isinstance(path, bytes):
|
||||
sep = b'/'
|
||||
curdir = b'.'
|
||||
pardir = b'..'
|
||||
else:
|
||||
sep = '/'
|
||||
curdir = '.'
|
||||
pardir = '..'
|
||||
|
||||
if isabs(rest):
|
||||
rest = rest[1:]
|
||||
path = sep
|
||||
|
||||
while rest:
|
||||
name, _, rest = rest.partition(sep)
|
||||
if not name or name == curdir:
|
||||
# current dir
|
||||
continue
|
||||
if name == pardir:
|
||||
# parent dir
|
||||
if path:
|
||||
path, name = split(path)
|
||||
if name == pardir:
|
||||
path = join(path, pardir, pardir)
|
||||
else:
|
||||
path = pardir
|
||||
continue
|
||||
newpath = join(path, name)
|
||||
if not islink(newpath):
|
||||
path = newpath
|
||||
continue
|
||||
# Resolve the symbolic link
|
||||
if newpath in seen:
|
||||
# Already seen this path
|
||||
path = seen[newpath]
|
||||
if path is not None:
|
||||
# use cached value
|
||||
continue
|
||||
# The symlink is not resolved, so we must have a symlink loop.
|
||||
# Return already resolved part + rest of the path unchanged.
|
||||
return join(newpath, rest), False
|
||||
seen[newpath] = None # not resolved symlink
|
||||
path, ok = _joinrealpath(path, os.readlink(newpath), seen)
|
||||
if not ok:
|
||||
return join(path, rest), False
|
||||
seen[newpath] = path # resolved symlink
|
||||
|
||||
return path, True
|
||||
|
||||
|
||||
supports_unicode_filenames = (sys.platform == 'darwin')
|
||||
|
||||
def relpath(path, start=None):
|
||||
"""Return a relative version of a path"""
|
||||
|
||||
if not path:
|
||||
raise ValueError("no path specified")
|
||||
|
||||
path = os.fspath(path)
|
||||
if isinstance(path, bytes):
|
||||
curdir = b'.'
|
||||
sep = b'/'
|
||||
pardir = b'..'
|
||||
else:
|
||||
curdir = '.'
|
||||
sep = '/'
|
||||
pardir = '..'
|
||||
|
||||
if start is None:
|
||||
start = curdir
|
||||
else:
|
||||
start = os.fspath(start)
|
||||
|
||||
try:
|
||||
start_list = [x for x in abspath(start).split(sep) if x]
|
||||
path_list = [x for x in abspath(path).split(sep) if x]
|
||||
# Work out how much of the filepath is shared by start and path.
|
||||
i = len(commonprefix([start_list, path_list]))
|
||||
|
||||
rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
|
||||
if not rel_list:
|
||||
return curdir
|
||||
return join(*rel_list)
|
||||
except (TypeError, AttributeError, BytesWarning, DeprecationWarning):
|
||||
genericpath._check_arg_types('relpath', path, start)
|
||||
raise
|
||||
|
||||
|
||||
# Return the longest common sub-path of the sequence of paths given as input.
|
||||
# The paths are not normalized before comparing them (this is the
|
||||
# responsibility of the caller). Any trailing separator is stripped from the
|
||||
# returned path.
|
||||
|
||||
def commonpath(paths):
|
||||
"""Given a sequence of path names, returns the longest common sub-path."""
|
||||
|
||||
if not paths:
|
||||
raise ValueError('commonpath() arg is an empty sequence')
|
||||
|
||||
paths = tuple(map(os.fspath, paths))
|
||||
if isinstance(paths[0], bytes):
|
||||
sep = b'/'
|
||||
curdir = b'.'
|
||||
else:
|
||||
sep = '/'
|
||||
curdir = '.'
|
||||
|
||||
try:
|
||||
split_paths = [path.split(sep) for path in paths]
|
||||
|
||||
try:
|
||||
isabs, = set(p[:1] == sep for p in paths)
|
||||
except ValueError:
|
||||
raise ValueError("Can't mix absolute and relative paths") from None
|
||||
|
||||
split_paths = [[c for c in s if c and c != curdir] for s in split_paths]
|
||||
s1 = min(split_paths)
|
||||
s2 = max(split_paths)
|
||||
common = s1
|
||||
for i, c in enumerate(s1):
|
||||
if c != s2[i]:
|
||||
common = s1[:i]
|
||||
break
|
||||
|
||||
prefix = sep if isabs else sep[:0]
|
||||
return prefix + sep.join(common)
|
||||
except (TypeError, AttributeError):
|
||||
genericpath._check_arg_types('commonpath', *paths)
|
||||
raise
|
||||
@@ -1,11 +0,0 @@
|
||||
|
||||
""" Regular expressions """
|
||||
|
||||
|
||||
def match(pattern, string, flags=0):
|
||||
return _compile(pattern, flags).match(string)
|
||||
|
||||
|
||||
def _compile(pattern, flags):
|
||||
p = sre_compile.compile(pattern, flags)
|
||||
return p
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
"""Redo the builtin repr() (representation) but with limits on most sizes."""
|
||||
|
||||
__all__ = ["Repr", "repr", "recursive_repr"]
|
||||
|
||||
import builtins
|
||||
from itertools import islice
|
||||
from _thread import get_ident
|
||||
|
||||
def recursive_repr(fillvalue='...'):
|
||||
'Decorator to make a repr function return fillvalue for a recursive call'
|
||||
|
||||
def decorating_function(user_function):
|
||||
repr_running = set()
|
||||
|
||||
def wrapper(self):
|
||||
key = id(self), get_ident()
|
||||
if key in repr_running:
|
||||
return fillvalue
|
||||
repr_running.add(key)
|
||||
try:
|
||||
result = user_function(self)
|
||||
finally:
|
||||
repr_running.discard(key)
|
||||
return result
|
||||
|
||||
# Can't use functools.wraps() here because of bootstrap issues
|
||||
wrapper.__module__ = getattr(user_function, '__module__')
|
||||
wrapper.__doc__ = getattr(user_function, '__doc__')
|
||||
wrapper.__name__ = getattr(user_function, '__name__')
|
||||
wrapper.__qualname__ = getattr(user_function, '__qualname__')
|
||||
wrapper.__annotations__ = getattr(user_function, '__annotations__', {})
|
||||
return wrapper
|
||||
|
||||
return decorating_function
|
||||
|
||||
class Repr:
|
||||
|
||||
def __init__(self):
|
||||
self.maxlevel = 6
|
||||
self.maxtuple = 6
|
||||
self.maxlist = 6
|
||||
self.maxarray = 5
|
||||
self.maxdict = 4
|
||||
self.maxset = 6
|
||||
self.maxfrozenset = 6
|
||||
self.maxdeque = 6
|
||||
self.maxstring = 30
|
||||
self.maxlong = 40
|
||||
self.maxother = 30
|
||||
|
||||
def repr(self, x):
|
||||
return self.repr1(x, self.maxlevel)
|
||||
|
||||
def repr1(self, x, level):
|
||||
typename = type(x).__name__
|
||||
if ' ' in typename:
|
||||
parts = typename.split()
|
||||
typename = '_'.join(parts)
|
||||
if hasattr(self, 'repr_' + typename):
|
||||
return getattr(self, 'repr_' + typename)(x, level)
|
||||
else:
|
||||
return self.repr_instance(x, level)
|
||||
|
||||
def _repr_iterable(self, x, level, left, right, maxiter, trail=''):
|
||||
n = len(x)
|
||||
if level <= 0 and n:
|
||||
s = '...'
|
||||
else:
|
||||
newlevel = level - 1
|
||||
repr1 = self.repr1
|
||||
pieces = [repr1(elem, newlevel) for elem in islice(x, maxiter)]
|
||||
if n > maxiter: pieces.append('...')
|
||||
s = ', '.join(pieces)
|
||||
if n == 1 and trail: right = trail + right
|
||||
return '%s%s%s' % (left, s, right)
|
||||
|
||||
def repr_tuple(self, x, level):
|
||||
return self._repr_iterable(x, level, '(', ')', self.maxtuple, ',')
|
||||
|
||||
def repr_list(self, x, level):
|
||||
return self._repr_iterable(x, level, '[', ']', self.maxlist)
|
||||
|
||||
def repr_array(self, x, level):
|
||||
if not x:
|
||||
return "array('%s')" % x.typecode
|
||||
header = "array('%s', [" % x.typecode
|
||||
return self._repr_iterable(x, level, header, '])', self.maxarray)
|
||||
|
||||
def repr_set(self, x, level):
|
||||
if not x:
|
||||
return 'set()'
|
||||
x = _possibly_sorted(x)
|
||||
return self._repr_iterable(x, level, '{', '}', self.maxset)
|
||||
|
||||
def repr_frozenset(self, x, level):
|
||||
if not x:
|
||||
return 'frozenset()'
|
||||
x = _possibly_sorted(x)
|
||||
return self._repr_iterable(x, level, 'frozenset({', '})',
|
||||
self.maxfrozenset)
|
||||
|
||||
def repr_deque(self, x, level):
|
||||
return self._repr_iterable(x, level, 'deque([', '])', self.maxdeque)
|
||||
|
||||
def repr_dict(self, x, level):
|
||||
n = len(x)
|
||||
if n == 0: return '{}'
|
||||
if level <= 0: return '{...}'
|
||||
newlevel = level - 1
|
||||
repr1 = self.repr1
|
||||
pieces = []
|
||||
for key in islice(_possibly_sorted(x), self.maxdict):
|
||||
keyrepr = repr1(key, newlevel)
|
||||
valrepr = repr1(x[key], newlevel)
|
||||
pieces.append('%s: %s' % (keyrepr, valrepr))
|
||||
if n > self.maxdict: pieces.append('...')
|
||||
s = ', '.join(pieces)
|
||||
return '{%s}' % (s,)
|
||||
|
||||
def repr_str(self, x, level):
|
||||
s = builtins.repr(x[:self.maxstring])
|
||||
if len(s) > self.maxstring:
|
||||
i = max(0, (self.maxstring-3)//2)
|
||||
j = max(0, self.maxstring-3-i)
|
||||
s = builtins.repr(x[:i] + x[len(x)-j:])
|
||||
s = s[:i] + '...' + s[len(s)-j:]
|
||||
return s
|
||||
|
||||
def repr_int(self, x, level):
|
||||
s = builtins.repr(x) # XXX Hope this isn't too slow...
|
||||
if len(s) > self.maxlong:
|
||||
i = max(0, (self.maxlong-3)//2)
|
||||
j = max(0, self.maxlong-3-i)
|
||||
s = s[:i] + '...' + s[len(s)-j:]
|
||||
return s
|
||||
|
||||
def repr_instance(self, x, level):
|
||||
try:
|
||||
s = builtins.repr(x)
|
||||
# Bugs in x.__repr__() can cause arbitrary
|
||||
# exceptions -- then make up something
|
||||
except Exception:
|
||||
return '<%s instance at %#x>' % (x.__class__.__name__, id(x))
|
||||
if len(s) > self.maxother:
|
||||
i = max(0, (self.maxother-3)//2)
|
||||
j = max(0, self.maxother-3-i)
|
||||
s = s[:i] + '...' + s[len(s)-j:]
|
||||
return s
|
||||
|
||||
|
||||
def _possibly_sorted(x):
|
||||
# Since not all sequences of items can be sorted and comparison
|
||||
# functions may raise arbitrary exceptions, return an unsorted
|
||||
# sequence in that case.
|
||||
try:
|
||||
return sorted(x)
|
||||
except Exception:
|
||||
return list(x)
|
||||
|
||||
aRepr = Repr()
|
||||
repr = aRepr.repr
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
"""Constants/functions for interpreting results of os.stat() and os.lstat().
|
||||
|
||||
Suggested usage: from stat import *
|
||||
"""
|
||||
|
||||
# Indices for stat struct members in the tuple returned by os.stat()
|
||||
|
||||
ST_MODE = 0
|
||||
ST_INO = 1
|
||||
ST_DEV = 2
|
||||
ST_NLINK = 3
|
||||
ST_UID = 4
|
||||
ST_GID = 5
|
||||
ST_SIZE = 6
|
||||
ST_ATIME = 7
|
||||
ST_MTIME = 8
|
||||
ST_CTIME = 9
|
||||
|
||||
# Extract bits from the mode
|
||||
|
||||
def S_IMODE(mode):
|
||||
"""Return the portion of the file's mode that can be set by
|
||||
os.chmod().
|
||||
"""
|
||||
return mode & 0o7777
|
||||
|
||||
def S_IFMT(mode):
|
||||
"""Return the portion of the file's mode that describes the
|
||||
file type.
|
||||
"""
|
||||
return mode & 0o170000
|
||||
|
||||
# Constants used as S_IFMT() for various file types
|
||||
# (not all are implemented on all systems)
|
||||
|
||||
S_IFDIR = 0o040000 # directory
|
||||
S_IFCHR = 0o020000 # character device
|
||||
S_IFBLK = 0o060000 # block device
|
||||
S_IFREG = 0o100000 # regular file
|
||||
S_IFIFO = 0o010000 # fifo (named pipe)
|
||||
S_IFLNK = 0o120000 # symbolic link
|
||||
S_IFSOCK = 0o140000 # socket file
|
||||
|
||||
# Functions to test for each file type
|
||||
|
||||
def S_ISDIR(mode):
|
||||
"""Return True if mode is from a directory."""
|
||||
return S_IFMT(mode) == S_IFDIR
|
||||
|
||||
def S_ISCHR(mode):
|
||||
"""Return True if mode is from a character special device file."""
|
||||
return S_IFMT(mode) == S_IFCHR
|
||||
|
||||
def S_ISBLK(mode):
|
||||
"""Return True if mode is from a block special device file."""
|
||||
return S_IFMT(mode) == S_IFBLK
|
||||
|
||||
def S_ISREG(mode):
|
||||
"""Return True if mode is from a regular file."""
|
||||
return S_IFMT(mode) == S_IFREG
|
||||
|
||||
def S_ISFIFO(mode):
|
||||
"""Return True if mode is from a FIFO (named pipe)."""
|
||||
return S_IFMT(mode) == S_IFIFO
|
||||
|
||||
def S_ISLNK(mode):
|
||||
"""Return True if mode is from a symbolic link."""
|
||||
return S_IFMT(mode) == S_IFLNK
|
||||
|
||||
def S_ISSOCK(mode):
|
||||
"""Return True if mode is from a socket."""
|
||||
return S_IFMT(mode) == S_IFSOCK
|
||||
|
||||
# Names for permission bits
|
||||
|
||||
S_ISUID = 0o4000 # set UID bit
|
||||
S_ISGID = 0o2000 # set GID bit
|
||||
S_ENFMT = S_ISGID # file locking enforcement
|
||||
S_ISVTX = 0o1000 # sticky bit
|
||||
S_IREAD = 0o0400 # Unix V7 synonym for S_IRUSR
|
||||
S_IWRITE = 0o0200 # Unix V7 synonym for S_IWUSR
|
||||
S_IEXEC = 0o0100 # Unix V7 synonym for S_IXUSR
|
||||
S_IRWXU = 0o0700 # mask for owner permissions
|
||||
S_IRUSR = 0o0400 # read by owner
|
||||
S_IWUSR = 0o0200 # write by owner
|
||||
S_IXUSR = 0o0100 # execute by owner
|
||||
S_IRWXG = 0o0070 # mask for group permissions
|
||||
S_IRGRP = 0o0040 # read by group
|
||||
S_IWGRP = 0o0020 # write by group
|
||||
S_IXGRP = 0o0010 # execute by group
|
||||
S_IRWXO = 0o0007 # mask for others (not in group) permissions
|
||||
S_IROTH = 0o0004 # read by others
|
||||
S_IWOTH = 0o0002 # write by others
|
||||
S_IXOTH = 0o0001 # execute by others
|
||||
|
||||
# Names for file flags
|
||||
|
||||
UF_NODUMP = 0x00000001 # do not dump file
|
||||
UF_IMMUTABLE = 0x00000002 # file may not be changed
|
||||
UF_APPEND = 0x00000004 # file may only be appended to
|
||||
UF_OPAQUE = 0x00000008 # directory is opaque when viewed through a union stack
|
||||
UF_NOUNLINK = 0x00000010 # file may not be renamed or deleted
|
||||
UF_COMPRESSED = 0x00000020 # OS X: file is hfs-compressed
|
||||
UF_HIDDEN = 0x00008000 # OS X: file should not be displayed
|
||||
SF_ARCHIVED = 0x00010000 # file may be archived
|
||||
SF_IMMUTABLE = 0x00020000 # file may not be changed
|
||||
SF_APPEND = 0x00040000 # file may only be appended to
|
||||
SF_NOUNLINK = 0x00100000 # file may not be renamed or deleted
|
||||
SF_SNAPSHOT = 0x00200000 # file is a snapshot file
|
||||
|
||||
|
||||
_filemode_table = (
|
||||
((S_IFLNK, "l"),
|
||||
(S_IFSOCK, "s"), # Must appear before IFREG and IFDIR as IFSOCK == IFREG | IFDIR
|
||||
(S_IFREG, "-"),
|
||||
(S_IFBLK, "b"),
|
||||
(S_IFDIR, "d"),
|
||||
(S_IFCHR, "c"),
|
||||
(S_IFIFO, "p")),
|
||||
|
||||
((S_IRUSR, "r"),),
|
||||
((S_IWUSR, "w"),),
|
||||
((S_IXUSR|S_ISUID, "s"),
|
||||
(S_ISUID, "S"),
|
||||
(S_IXUSR, "x")),
|
||||
|
||||
((S_IRGRP, "r"),),
|
||||
((S_IWGRP, "w"),),
|
||||
((S_IXGRP|S_ISGID, "s"),
|
||||
(S_ISGID, "S"),
|
||||
(S_IXGRP, "x")),
|
||||
|
||||
((S_IROTH, "r"),),
|
||||
((S_IWOTH, "w"),),
|
||||
((S_IXOTH|S_ISVTX, "t"),
|
||||
(S_ISVTX, "T"),
|
||||
(S_IXOTH, "x"))
|
||||
)
|
||||
|
||||
def filemode(mode):
|
||||
"""Convert a file's mode to a string of the form '-rwxrwxrwx'."""
|
||||
perm = []
|
||||
for table in _filemode_table:
|
||||
for bit, char in table:
|
||||
if mode & bit == bit:
|
||||
perm.append(char)
|
||||
break
|
||||
else:
|
||||
perm.append("-")
|
||||
return "".join(perm)
|
||||
|
||||
|
||||
# Windows FILE_ATTRIBUTE constants for interpreting os.stat()'s
|
||||
# "st_file_attributes" member
|
||||
|
||||
FILE_ATTRIBUTE_ARCHIVE = 32
|
||||
FILE_ATTRIBUTE_COMPRESSED = 2048
|
||||
FILE_ATTRIBUTE_DEVICE = 64
|
||||
FILE_ATTRIBUTE_DIRECTORY = 16
|
||||
FILE_ATTRIBUTE_ENCRYPTED = 16384
|
||||
FILE_ATTRIBUTE_HIDDEN = 2
|
||||
FILE_ATTRIBUTE_INTEGRITY_STREAM = 32768
|
||||
FILE_ATTRIBUTE_NORMAL = 128
|
||||
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192
|
||||
FILE_ATTRIBUTE_NO_SCRUB_DATA = 131072
|
||||
FILE_ATTRIBUTE_OFFLINE = 4096
|
||||
FILE_ATTRIBUTE_READONLY = 1
|
||||
FILE_ATTRIBUTE_REPARSE_POINT = 1024
|
||||
FILE_ATTRIBUTE_SPARSE_FILE = 512
|
||||
FILE_ATTRIBUTE_SYSTEM = 4
|
||||
FILE_ATTRIBUTE_TEMPORARY = 256
|
||||
FILE_ATTRIBUTE_VIRTUAL = 65536
|
||||
|
||||
|
||||
# If available, use C implementation
|
||||
try:
|
||||
from _stat import *
|
||||
except ImportError:
|
||||
pass
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
"""
|
||||
Define names for built-in types that aren't directly accessible as a builtin.
|
||||
"""
|
||||
import sys
|
||||
|
||||
# Iterators in Python aren't a matter of type but of protocol. A large
|
||||
# and changing number of builtin types implement *some* flavor of
|
||||
# iterator. Don't check the type! Use hasattr to check for both
|
||||
# "__iter__" and "__next__" attributes instead.
|
||||
|
||||
def _f(): pass
|
||||
FunctionType = type(_f)
|
||||
LambdaType = type(lambda: None) # Same as FunctionType
|
||||
CodeType = type(_f.__code__)
|
||||
MappingProxyType = type(type.__dict__)
|
||||
SimpleNamespace = type(sys.implementation)
|
||||
|
||||
def _g():
|
||||
yield 1
|
||||
GeneratorType = type(_g())
|
||||
|
||||
# async def _c(): pass
|
||||
# _c = _c()
|
||||
# CoroutineType = type(_c)
|
||||
# _c.close() # Prevent ResourceWarning
|
||||
|
||||
# async def _ag():
|
||||
# yield
|
||||
# _ag = _ag()
|
||||
# AsyncGeneratorType = type(_ag)
|
||||
|
||||
class _C:
|
||||
def _m(self): pass
|
||||
MethodType = type(_C()._m)
|
||||
|
||||
BuiltinFunctionType = type(len)
|
||||
BuiltinMethodType = type([].append) # Same as BuiltinFunctionType
|
||||
|
||||
WrapperDescriptorType = type(object.__init__)
|
||||
MethodWrapperType = type(object().__str__)
|
||||
MethodDescriptorType = type(str.join)
|
||||
ClassMethodDescriptorType = type(dict.__dict__['fromkeys'])
|
||||
|
||||
ModuleType = type(sys)
|
||||
|
||||
# try:
|
||||
# raise TypeError
|
||||
# except TypeError:
|
||||
# tb = sys.exc_info()[2]
|
||||
# TracebackType = type(tb)
|
||||
# FrameType = type(tb.tb_frame)
|
||||
# tb = None; del tb
|
||||
|
||||
# For Jython, the following two types are identical
|
||||
GetSetDescriptorType = type(FunctionType.__code__)
|
||||
# MemberDescriptorType = type(FunctionType.__globals__)
|
||||
|
||||
del sys, _f, _g, _C # Not for export
|
||||
|
||||
|
||||
# Provide a PEP 3115 compliant mechanism for class creation
|
||||
def new_class(name, bases=(), kwds=None, exec_body=None):
|
||||
"""Create a class object dynamically using the appropriate metaclass."""
|
||||
resolved_bases = resolve_bases(bases)
|
||||
meta, ns, kwds = prepare_class(name, resolved_bases, kwds)
|
||||
if exec_body is not None:
|
||||
exec_body(ns)
|
||||
if resolved_bases is not bases:
|
||||
ns['__orig_bases__'] = bases
|
||||
return meta(name, resolved_bases, ns, **kwds)
|
||||
|
||||
def resolve_bases(bases):
|
||||
"""Resolve MRO entries dynamically as specified by PEP 560."""
|
||||
new_bases = list(bases)
|
||||
updated = False
|
||||
shift = 0
|
||||
for i, base in enumerate(bases):
|
||||
if isinstance(base, type):
|
||||
continue
|
||||
if not hasattr(base, "__mro_entries__"):
|
||||
continue
|
||||
new_base = base.__mro_entries__(bases)
|
||||
updated = True
|
||||
if not isinstance(new_base, tuple):
|
||||
raise TypeError("__mro_entries__ must return a tuple")
|
||||
else:
|
||||
new_bases[i+shift:i+shift+1] = new_base
|
||||
shift += len(new_base) - 1
|
||||
if not updated:
|
||||
return bases
|
||||
return tuple(new_bases)
|
||||
|
||||
def prepare_class(name, bases=(), kwds=None):
|
||||
"""Call the __prepare__ method of the appropriate metaclass.
|
||||
|
||||
Returns (metaclass, namespace, kwds) as a 3-tuple
|
||||
|
||||
*metaclass* is the appropriate metaclass
|
||||
*namespace* is the prepared class namespace
|
||||
*kwds* is an updated copy of the passed in kwds argument with any
|
||||
'metaclass' entry removed. If no kwds argument is passed in, this will
|
||||
be an empty dict.
|
||||
"""
|
||||
if kwds is None:
|
||||
kwds = {}
|
||||
else:
|
||||
kwds = dict(kwds) # Don't alter the provided mapping
|
||||
if 'metaclass' in kwds:
|
||||
meta = kwds.pop('metaclass')
|
||||
else:
|
||||
if bases:
|
||||
meta = type(bases[0])
|
||||
else:
|
||||
meta = type
|
||||
if isinstance(meta, type):
|
||||
# when meta is a type, we first determine the most-derived metaclass
|
||||
# instead of invoking the initial candidate directly
|
||||
meta = _calculate_meta(meta, bases)
|
||||
if hasattr(meta, '__prepare__'):
|
||||
ns = meta.__prepare__(name, bases, **kwds)
|
||||
else:
|
||||
ns = {}
|
||||
return meta, ns, kwds
|
||||
|
||||
def _calculate_meta(meta, bases):
|
||||
"""Calculate the most derived metaclass."""
|
||||
winner = meta
|
||||
for base in bases:
|
||||
base_meta = type(base)
|
||||
if issubclass(winner, base_meta):
|
||||
continue
|
||||
if issubclass(base_meta, winner):
|
||||
winner = base_meta
|
||||
continue
|
||||
# else:
|
||||
raise TypeError("metaclass conflict: "
|
||||
"the metaclass of a derived class "
|
||||
"must be a (non-strict) subclass "
|
||||
"of the metaclasses of all its bases")
|
||||
return winner
|
||||
|
||||
class DynamicClassAttribute:
|
||||
"""Route attribute access on a class to __getattr__.
|
||||
|
||||
This is a descriptor, used to define attributes that act differently when
|
||||
accessed through an instance and through a class. Instance access remains
|
||||
normal, but access to an attribute through a class will be routed to the
|
||||
class's __getattr__ method; this is done by raising AttributeError.
|
||||
|
||||
This allows one to have properties active on an instance, and have virtual
|
||||
attributes on the class with the same name (see Enum for an example).
|
||||
|
||||
"""
|
||||
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
|
||||
self.fget = fget
|
||||
self.fset = fset
|
||||
self.fdel = fdel
|
||||
# next two lines make DynamicClassAttribute act the same as property
|
||||
self.__doc__ = doc or fget.__doc__
|
||||
self.overwrite_doc = doc is None
|
||||
# support for abstract methods
|
||||
self.__isabstractmethod__ = bool(getattr(fget, '__isabstractmethod__', False))
|
||||
|
||||
def __get__(self, instance, ownerclass=None):
|
||||
if instance is None:
|
||||
if self.__isabstractmethod__:
|
||||
return self
|
||||
raise AttributeError()
|
||||
elif self.fget is None:
|
||||
raise AttributeError("unreadable attribute")
|
||||
return self.fget(instance)
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if self.fset is None:
|
||||
raise AttributeError("can't set attribute")
|
||||
self.fset(instance, value)
|
||||
|
||||
def __delete__(self, instance):
|
||||
if self.fdel is None:
|
||||
raise AttributeError("can't delete attribute")
|
||||
self.fdel(instance)
|
||||
|
||||
def getter(self, fget):
|
||||
fdoc = fget.__doc__ if self.overwrite_doc else None
|
||||
result = type(self)(fget, self.fset, self.fdel, fdoc or self.__doc__)
|
||||
result.overwrite_doc = self.overwrite_doc
|
||||
return result
|
||||
|
||||
def setter(self, fset):
|
||||
result = type(self)(self.fget, fset, self.fdel, self.__doc__)
|
||||
result.overwrite_doc = self.overwrite_doc
|
||||
return result
|
||||
|
||||
def deleter(self, fdel):
|
||||
result = type(self)(self.fget, self.fset, fdel, self.__doc__)
|
||||
result.overwrite_doc = self.overwrite_doc
|
||||
return result
|
||||
|
||||
|
||||
class _GeneratorWrapper:
|
||||
# TODO: Implement this in C.
|
||||
def __init__(self, gen):
|
||||
self.__wrapped = gen
|
||||
self.__isgen = gen.__class__ is GeneratorType
|
||||
self.__name__ = getattr(gen, '__name__', None)
|
||||
self.__qualname__ = getattr(gen, '__qualname__', None)
|
||||
def send(self, val):
|
||||
return self.__wrapped.send(val)
|
||||
def throw(self, tp, *rest):
|
||||
return self.__wrapped.throw(tp, *rest)
|
||||
def close(self):
|
||||
return self.__wrapped.close()
|
||||
@property
|
||||
def gi_code(self):
|
||||
return self.__wrapped.gi_code
|
||||
@property
|
||||
def gi_frame(self):
|
||||
return self.__wrapped.gi_frame
|
||||
@property
|
||||
def gi_running(self):
|
||||
return self.__wrapped.gi_running
|
||||
@property
|
||||
def gi_yieldfrom(self):
|
||||
return self.__wrapped.gi_yieldfrom
|
||||
cr_code = gi_code
|
||||
cr_frame = gi_frame
|
||||
cr_running = gi_running
|
||||
cr_await = gi_yieldfrom
|
||||
def __next__(self):
|
||||
return next(self.__wrapped)
|
||||
def __iter__(self):
|
||||
if self.__isgen:
|
||||
return self.__wrapped
|
||||
return self
|
||||
__await__ = __iter__
|
||||
|
||||
def coroutine(func):
|
||||
"""Convert regular generator function to a coroutine."""
|
||||
|
||||
if not callable(func):
|
||||
raise TypeError('types.coroutine() expects a callable')
|
||||
|
||||
if (func.__class__ is FunctionType and
|
||||
getattr(func, '__code__', None).__class__ is CodeType):
|
||||
|
||||
co_flags = func.__code__.co_flags
|
||||
|
||||
# Check if 'func' is a coroutine function.
|
||||
# (0x180 == CO_COROUTINE | CO_ITERABLE_COROUTINE)
|
||||
if co_flags & 0x180:
|
||||
return func
|
||||
|
||||
# Check if 'func' is a generator function.
|
||||
# (0x20 == CO_GENERATOR)
|
||||
if co_flags & 0x20:
|
||||
# TODO: Implement this in C.
|
||||
co = func.__code__
|
||||
func.__code__ = CodeType(
|
||||
co.co_argcount, co.co_kwonlyargcount, co.co_nlocals,
|
||||
co.co_stacksize,
|
||||
co.co_flags | 0x100, # 0x100 == CO_ITERABLE_COROUTINE
|
||||
co.co_code,
|
||||
co.co_consts, co.co_names, co.co_varnames, co.co_filename,
|
||||
co.co_name, co.co_firstlineno, co.co_lnotab, co.co_freevars,
|
||||
co.co_cellvars)
|
||||
return func
|
||||
|
||||
# The following code is primarily to support functions that
|
||||
# return generator-like objects (for instance generators
|
||||
# compiled with Cython).
|
||||
|
||||
# Delay functools and _collections_abc import for speeding up types import.
|
||||
import functools
|
||||
import _collections_abc
|
||||
@functools.wraps(func)
|
||||
def wrapped(*args, **kwargs):
|
||||
coro = func(*args, **kwargs)
|
||||
if (coro.__class__ is CoroutineType or
|
||||
coro.__class__ is GeneratorType and coro.gi_code.co_flags & 0x100):
|
||||
# 'coro' is a native coroutine object or an iterable coroutine
|
||||
return coro
|
||||
if (isinstance(coro, _collections_abc.Generator) and
|
||||
not isinstance(coro, _collections_abc.Coroutine)):
|
||||
# 'coro' is either a pure Python generator iterator, or it
|
||||
# implements collections.abc.Generator (and does not implement
|
||||
# collections.abc.Coroutine).
|
||||
return _GeneratorWrapper(coro)
|
||||
# 'coro' is either an instance of collections.abc.Coroutine or
|
||||
# some other object -- pass it through.
|
||||
return coro
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
__all__ = [n for n in globals() if n[:1] != '_']
|
||||
@@ -85,7 +85,7 @@ Another approach is to checkout the source code: builtin functions and object me
|
||||
and easiest way to contribute.
|
||||
|
||||
You can also simply run
|
||||
`cargo run tests/snippets/whats_left_to_implement.py` to assist in finding any
|
||||
`./whats_left.sh` to assist in finding any
|
||||
unimplemented method.
|
||||
|
||||
# Testing
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
# Benchmarking
|
||||
|
||||
These are some files to determine performance of rustpython.
|
||||
|
||||
# Usage
|
||||
|
||||
Install pytest and pytest-benchmark:
|
||||
|
||||
$ pip install pytest-benchmark
|
||||
|
||||
Then run:
|
||||
|
||||
$ pytest
|
||||
|
||||
# Benchmark source
|
||||
|
||||
- https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/nbody-python3-2.html
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate cpython;
|
||||
extern crate rustpython_parser;
|
||||
extern crate rustpython_vm;
|
||||
extern crate test;
|
||||
|
||||
use rustpython_vm::pyobject::PyResult;
|
||||
use rustpython_vm::{compile, VirtualMachine};
|
||||
|
||||
#[bench]
|
||||
fn bench_tokenization(b: &mut test::Bencher) {
|
||||
use rustpython_parser::lexer::{make_tokenizer, Tok};
|
||||
|
||||
let source = include_str!("./benchmarks/minidom.py");
|
||||
|
||||
b.bytes = source.len() as _;
|
||||
b.iter(|| {
|
||||
let lexer = make_tokenizer(source);
|
||||
for res in lexer {
|
||||
let _token: Tok = res.unwrap().1;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_rustpy_parse_to_ast(b: &mut test::Bencher) {
|
||||
use rustpython_parser::parser::parse_program;
|
||||
|
||||
let source = include_str!("./benchmarks/minidom.py");
|
||||
|
||||
b.bytes = source.len() as _;
|
||||
b.iter(|| parse_program(source).unwrap())
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_cpython_parse_to_ast(b: &mut test::Bencher) {
|
||||
let source = include_str!("./benchmarks/minidom.py");
|
||||
|
||||
let gil = cpython::Python::acquire_gil();
|
||||
let python = gil.python();
|
||||
|
||||
let globals = None;
|
||||
let locals = cpython::PyDict::new(python);
|
||||
|
||||
locals.set_item(python, "SOURCE_CODE", source).unwrap();
|
||||
|
||||
let code = "compile(SOURCE_CODE, mode=\"exec\", filename=\"minidom.py\")";
|
||||
|
||||
b.bytes = source.len() as _;
|
||||
b.iter(|| {
|
||||
let res: cpython::PyResult<cpython::PyObject> = python.eval(code, globals, Some(&locals));
|
||||
assert!(res.is_ok());
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_cpython_nbody(b: &mut test::Bencher) {
|
||||
let source = include_str!("./benchmarks/nbody.py");
|
||||
|
||||
let gil = cpython::Python::acquire_gil();
|
||||
let python = gil.python();
|
||||
|
||||
let globals = None;
|
||||
let locals = None;
|
||||
|
||||
b.iter(|| {
|
||||
let res: cpython::PyResult<()> = python.run(source, globals, locals);
|
||||
assert!(res.is_ok());
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_cpython_mandelbrot(b: &mut test::Bencher) {
|
||||
let source = include_str!("./benchmarks/mandelbrot.py");
|
||||
|
||||
let gil = cpython::Python::acquire_gil();
|
||||
let python = gil.python();
|
||||
|
||||
let globals = None;
|
||||
let locals = None;
|
||||
|
||||
b.iter(|| {
|
||||
let res: cpython::PyResult<()> = python.run(source, globals, locals);
|
||||
assert!(res.is_ok());
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_rustpy_nbody(b: &mut test::Bencher) {
|
||||
// NOTE: Take long time.
|
||||
let source = include_str!("./benchmarks/nbody.py");
|
||||
|
||||
let vm = VirtualMachine::new();
|
||||
|
||||
let code = match compile::compile(&vm, source, &compile::Mode::Single, "<stdin>".to_string()) {
|
||||
Ok(code) => code,
|
||||
Err(e) => panic!("{:?}", e),
|
||||
};
|
||||
|
||||
b.iter(|| {
|
||||
let scope = vm.new_scope_with_builtins();
|
||||
let res: PyResult = vm.run_code_obj(code.clone(), scope);
|
||||
assert!(res.is_ok());
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_rustpy_mandelbrot(b: &mut test::Bencher) {
|
||||
// NOTE: Take long time.
|
||||
let source = include_str!("./benchmarks/mandelbrot.py");
|
||||
|
||||
let vm = VirtualMachine::new();
|
||||
|
||||
let code = match compile::compile(&vm, source, &compile::Mode::Single, "<stdin>".to_string()) {
|
||||
Ok(code) => code,
|
||||
Err(e) => panic!("{:?}", e),
|
||||
};
|
||||
|
||||
b.iter(|| {
|
||||
let scope = vm.new_scope_with_builtins();
|
||||
let res: PyResult = vm.run_code_obj(code.clone(), scope);
|
||||
assert!(res.is_ok());
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
# coding: utf-8
|
||||
|
||||
w = 50.0
|
||||
h = 50.0
|
||||
|
||||
y = 0.0
|
||||
while y < h:
|
||||
x = 0.0
|
||||
while x < w:
|
||||
Zr, Zi, Tr, Ti = 0.0, 0.0, 0.0, 0.0
|
||||
Cr = 2*x/w - 1.5
|
||||
Ci = 2*y/h - 1.0
|
||||
|
||||
i = 0
|
||||
while i < 50 and Tr+Ti <= 4:
|
||||
Zi = 2*Zr*Zi + Ci
|
||||
Zr = Tr - Ti + Cr
|
||||
Tr = Zr * Zr
|
||||
Ti = Zi * Zi
|
||||
i = i+1
|
||||
|
||||
if Tr+Ti <= 4:
|
||||
# print('*', end='')
|
||||
pass
|
||||
else:
|
||||
# print('·', end='')
|
||||
pass
|
||||
|
||||
x = x+1
|
||||
|
||||
# print()
|
||||
y = y+1
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,110 @@
|
||||
|
||||
# The Computer Language Benchmarks Game
|
||||
# https://salsa.debian.org/benchmarksgame-team/benchmarksgame/
|
||||
#
|
||||
# originally by Kevin Carson
|
||||
# modified by Tupteq, Fredrik Johansson, and Daniel Nanz
|
||||
# modified by Maciej Fijalkowski
|
||||
# 2to3
|
||||
# modified by Andriy Misyura
|
||||
|
||||
from math import sqrt
|
||||
|
||||
def combinations(l):
|
||||
result = []
|
||||
for x in range(len(l) - 1):
|
||||
ls = l[x+1:]
|
||||
for y in ls:
|
||||
result.append((l[x][0],l[x][1],l[x][2],y[0],y[1],y[2]))
|
||||
return result
|
||||
|
||||
PI = 3.14159265358979323
|
||||
SOLAR_MASS = 4 * PI * PI
|
||||
DAYS_PER_YEAR = 365.24
|
||||
|
||||
BODIES = {
|
||||
'sun': ([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], SOLAR_MASS),
|
||||
|
||||
'jupiter': ([4.84143144246472090e+00,
|
||||
-1.16032004402742839e+00,
|
||||
-1.03622044471123109e-01],
|
||||
[1.66007664274403694e-03 * DAYS_PER_YEAR,
|
||||
7.69901118419740425e-03 * DAYS_PER_YEAR,
|
||||
-6.90460016972063023e-05 * DAYS_PER_YEAR],
|
||||
9.54791938424326609e-04 * SOLAR_MASS),
|
||||
|
||||
'saturn': ([8.34336671824457987e+00,
|
||||
4.12479856412430479e+00,
|
||||
-4.03523417114321381e-01],
|
||||
[-2.76742510726862411e-03 * DAYS_PER_YEAR,
|
||||
4.99852801234917238e-03 * DAYS_PER_YEAR,
|
||||
2.30417297573763929e-05 * DAYS_PER_YEAR],
|
||||
2.85885980666130812e-04 * SOLAR_MASS),
|
||||
|
||||
'uranus': ([1.28943695621391310e+01,
|
||||
-1.51111514016986312e+01,
|
||||
-2.23307578892655734e-01],
|
||||
[2.96460137564761618e-03 * DAYS_PER_YEAR,
|
||||
2.37847173959480950e-03 * DAYS_PER_YEAR,
|
||||
-2.96589568540237556e-05 * DAYS_PER_YEAR],
|
||||
4.36624404335156298e-05 * SOLAR_MASS),
|
||||
|
||||
'neptune': ([1.53796971148509165e+01,
|
||||
-2.59193146099879641e+01,
|
||||
1.79258772950371181e-01],
|
||||
[2.68067772490389322e-03 * DAYS_PER_YEAR,
|
||||
1.62824170038242295e-03 * DAYS_PER_YEAR,
|
||||
-9.51592254519715870e-05 * DAYS_PER_YEAR],
|
||||
5.15138902046611451e-05 * SOLAR_MASS) }
|
||||
|
||||
SYSTEM = tuple(BODIES.values())
|
||||
PAIRS = tuple(combinations(SYSTEM))
|
||||
|
||||
def advance(dt, n, bodies=SYSTEM, pairs=PAIRS):
|
||||
for i in range(n):
|
||||
for ([x1, y1, z1], v1, m1, [x2, y2, z2], v2, m2) in pairs:
|
||||
dx = x1 - x2
|
||||
dy = y1 - y2
|
||||
dz = z1 - z2
|
||||
dist = sqrt(dx * dx + dy * dy + dz * dz);
|
||||
mag = dt / (dist*dist*dist)
|
||||
b1m = m1 * mag
|
||||
b2m = m2 * mag
|
||||
v1[0] -= dx * b2m
|
||||
v1[1] -= dy * b2m
|
||||
v1[2] -= dz * b2m
|
||||
v2[2] += dz * b1m
|
||||
v2[1] += dy * b1m
|
||||
v2[0] += dx * b1m
|
||||
for (r, [vx, vy, vz], m) in bodies:
|
||||
r[0] += dt * vx
|
||||
r[1] += dt * vy
|
||||
r[2] += dt * vz
|
||||
|
||||
def report_energy(bodies=SYSTEM, pairs=PAIRS, e=0.0):
|
||||
for ((x1, y1, z1), v1, m1, (x2, y2, z2), v2, m2) in pairs:
|
||||
dx = x1 - x2
|
||||
dy = y1 - y2
|
||||
dz = z1 - z2
|
||||
e -= (m1 * m2) / ((dx * dx + dy * dy + dz * dz) ** 0.5)
|
||||
for (r, [vx, vy, vz], m) in bodies:
|
||||
e += m * (vx * vx + vy * vy + vz * vz) / 2.
|
||||
# print(f"{e}")
|
||||
|
||||
def offset_momentum(ref, bodies=SYSTEM, px=0.0, py=0.0, pz=0.0):
|
||||
for (r, [vx, vy, vz], m) in bodies:
|
||||
px -= vx * m
|
||||
py -= vy * m
|
||||
pz -= vz * m
|
||||
(r, v, m) = ref
|
||||
v[0] = px / m
|
||||
v[1] = py / m
|
||||
v[2] = pz / m
|
||||
|
||||
def main(n, ref='sun'):
|
||||
offset_momentum(BODIES[ref])
|
||||
report_energy()
|
||||
advance(0.01, n)
|
||||
report_energy()
|
||||
|
||||
main(500)
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
import time
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
import subprocess
|
||||
|
||||
from benchmarks import nbody
|
||||
|
||||
# Interpreters:
|
||||
rustpython_exe = '../target/release/rustpython'
|
||||
cpython_exe = sys.executable
|
||||
pythons = [
|
||||
cpython_exe,
|
||||
rustpython_exe
|
||||
]
|
||||
|
||||
# Benchmark scripts:
|
||||
benchmarks = [
|
||||
['benchmarks/nbody.py'],
|
||||
['benchmarks/mandelbrot.py'],
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize('exe', pythons)
|
||||
@pytest.mark.parametrize('args', benchmarks)
|
||||
def test_bench(exe, args, benchmark):
|
||||
def bench():
|
||||
subprocess.run([exe] + args)
|
||||
|
||||
benchmark(bench)
|
||||
|
||||
+1
-1
@@ -8,6 +8,6 @@ edition = "2018"
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "0.15.29"
|
||||
syn = { version = "0.15.29", features = ["full"] }
|
||||
quote = "0.6.11"
|
||||
proc-macro2 = "0.4.27"
|
||||
@@ -0,0 +1,158 @@
|
||||
// Taken from https://github.com/rustwasm/wasm-bindgen/blob/master/crates/backend/src/error.rs
|
||||
//
|
||||
// Copyright (c) 2014 Alex Crichton
|
||||
//
|
||||
// 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.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use proc_macro2::*;
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
use syn::parse::Error;
|
||||
|
||||
macro_rules! err_span {
|
||||
($span:expr, $($msg:tt)*) => (
|
||||
$crate::Diagnostic::spanned_error(&$span, format!($($msg)*))
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! bail_span {
|
||||
($($t:tt)*) => (
|
||||
return Err(err_span!($($t)*).into())
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! push_err_span {
|
||||
($diagnostics:expr, $($t:tt)*) => {
|
||||
$diagnostics.push(err_span!($($t)*))
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Diagnostic {
|
||||
inner: Repr,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Repr {
|
||||
Single {
|
||||
text: String,
|
||||
span: Option<(Span, Span)>,
|
||||
},
|
||||
SynError(Error),
|
||||
Multi {
|
||||
diagnostics: Vec<Diagnostic>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Diagnostic {
|
||||
pub fn error<T: Into<String>>(text: T) -> Diagnostic {
|
||||
Diagnostic {
|
||||
inner: Repr::Single {
|
||||
text: text.into(),
|
||||
span: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span_error<T: Into<String>>(span: Span, text: T) -> Diagnostic {
|
||||
Diagnostic {
|
||||
inner: Repr::Single {
|
||||
text: text.into(),
|
||||
span: Some((span, span)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spanned_error<T: Into<String>>(node: &ToTokens, text: T) -> Diagnostic {
|
||||
Diagnostic {
|
||||
inner: Repr::Single {
|
||||
text: text.into(),
|
||||
span: extract_spans(node),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_vec(diagnostics: Vec<Diagnostic>) -> Result<(), Diagnostic> {
|
||||
if diagnostics.len() == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Diagnostic {
|
||||
inner: Repr::Multi { diagnostics },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unconditional_recursion)]
|
||||
pub fn panic(&self) -> ! {
|
||||
match &self.inner {
|
||||
Repr::Single { text, .. } => panic!("{}", text),
|
||||
Repr::SynError(error) => panic!("{}", error),
|
||||
Repr::Multi { diagnostics } => diagnostics[0].panic(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for Diagnostic {
|
||||
fn from(err: Error) -> Diagnostic {
|
||||
Diagnostic {
|
||||
inner: Repr::SynError(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_spans(node: &ToTokens) -> Option<(Span, Span)> {
|
||||
let mut t = TokenStream::new();
|
||||
node.to_tokens(&mut t);
|
||||
let mut tokens = t.into_iter();
|
||||
let start = tokens.next().map(|t| t.span());
|
||||
let end = tokens.last().map(|t| t.span());
|
||||
start.map(|start| (start, end.unwrap_or(start)))
|
||||
}
|
||||
|
||||
impl ToTokens for Diagnostic {
|
||||
fn to_tokens(&self, dst: &mut TokenStream) {
|
||||
match &self.inner {
|
||||
Repr::Single { text, span } => {
|
||||
let cs2 = (Span::call_site(), Span::call_site());
|
||||
let (start, end) = span.unwrap_or(cs2);
|
||||
dst.append(Ident::new("compile_error", start));
|
||||
dst.append(Punct::new('!', Spacing::Alone));
|
||||
let mut message = TokenStream::new();
|
||||
message.append(Literal::string(text));
|
||||
let mut group = Group::new(Delimiter::Brace, message);
|
||||
group.set_span(end);
|
||||
dst.append(group);
|
||||
}
|
||||
Repr::Multi { diagnostics } => {
|
||||
for diagnostic in diagnostics {
|
||||
diagnostic.to_tokens(dst);
|
||||
}
|
||||
}
|
||||
Repr::SynError(err) => {
|
||||
err.to_compile_error().to_tokens(dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use syn::{Attribute, Data, DeriveInput, Expr, Field, Fields, Ident, Lit, Meta, NestedMeta};
|
||||
|
||||
/// The kind of the python parameter, this corresponds to the value of Parameter.kind
|
||||
/// (https://docs.python.org/3/library/inspect.html#inspect.Parameter.kind)
|
||||
enum ParameterKind {
|
||||
PositionalOnly,
|
||||
PositionalOrKeyword,
|
||||
KeywordOnly,
|
||||
}
|
||||
|
||||
impl ParameterKind {
|
||||
fn from_ident(ident: &Ident) -> ParameterKind {
|
||||
if ident == "positional_only" {
|
||||
ParameterKind::PositionalOnly
|
||||
} else if ident == "positional_or_keyword" {
|
||||
ParameterKind::PositionalOrKeyword
|
||||
} else if ident == "keyword_only" {
|
||||
ParameterKind::KeywordOnly
|
||||
} else {
|
||||
panic!("Unrecognised attribute")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ArgAttribute {
|
||||
kind: ParameterKind,
|
||||
default: Option<Expr>,
|
||||
optional: bool,
|
||||
}
|
||||
|
||||
impl ArgAttribute {
|
||||
fn from_attribute(attr: &Attribute) -> Option<ArgAttribute> {
|
||||
if !attr.path.is_ident("pyarg") {
|
||||
return None;
|
||||
}
|
||||
|
||||
match attr.parse_meta().unwrap() {
|
||||
Meta::List(list) => {
|
||||
let mut iter = list.nested.iter();
|
||||
let first_arg = iter.next().expect("at least one argument in pyarg list");
|
||||
let kind = match first_arg {
|
||||
NestedMeta::Meta(Meta::Word(ident)) => ParameterKind::from_ident(ident),
|
||||
_ => panic!("Bad syntax for first pyarg attribute argument"),
|
||||
};
|
||||
|
||||
let mut attribute = ArgAttribute {
|
||||
kind,
|
||||
default: None,
|
||||
optional: false,
|
||||
};
|
||||
|
||||
while let Some(arg) = iter.next() {
|
||||
attribute.parse_argument(arg);
|
||||
}
|
||||
|
||||
assert!(
|
||||
attribute.default.is_none() || !attribute.optional,
|
||||
"Can't set both a default value and optional"
|
||||
);
|
||||
|
||||
Some(attribute)
|
||||
}
|
||||
_ => panic!("Bad syntax for pyarg attribute"),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_argument(&mut self, arg: &NestedMeta) {
|
||||
match arg {
|
||||
NestedMeta::Meta(Meta::Word(ident)) => {
|
||||
if ident == "default" {
|
||||
assert!(self.default.is_none(), "Default already set");
|
||||
let expr = syn::parse_str::<Expr>("Default::default()").unwrap();
|
||||
self.default = Some(expr);
|
||||
} else if ident == "optional" {
|
||||
self.optional = true;
|
||||
} else {
|
||||
panic!("Unrecognised pyarg attribute '{}'", ident);
|
||||
}
|
||||
}
|
||||
NestedMeta::Meta(Meta::NameValue(name_value)) => {
|
||||
if name_value.ident == "default" {
|
||||
assert!(self.default.is_none(), "Default already set");
|
||||
|
||||
match name_value.lit {
|
||||
Lit::Str(ref val) => {
|
||||
let expr = val
|
||||
.parse::<Expr>()
|
||||
.expect("a valid expression for default argument");
|
||||
self.default = Some(expr);
|
||||
}
|
||||
_ => panic!("Expected string value for default argument"),
|
||||
}
|
||||
} else if name_value.ident == "optional" {
|
||||
match name_value.lit {
|
||||
Lit::Bool(ref val) => {
|
||||
self.optional = val.value;
|
||||
}
|
||||
_ => panic!("Expected boolean value for optional argument"),
|
||||
}
|
||||
} else {
|
||||
panic!("Unrecognised pyarg attribute '{}'", name_value.ident);
|
||||
}
|
||||
}
|
||||
_ => panic!("Bad syntax for first pyarg attribute argument"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_field(field: &Field) -> TokenStream2 {
|
||||
let mut pyarg_attrs = field
|
||||
.attrs
|
||||
.iter()
|
||||
.filter_map(ArgAttribute::from_attribute)
|
||||
.collect::<Vec<_>>();
|
||||
let attr = if pyarg_attrs.is_empty() {
|
||||
ArgAttribute {
|
||||
kind: ParameterKind::PositionalOrKeyword,
|
||||
default: None,
|
||||
optional: false,
|
||||
}
|
||||
} else if pyarg_attrs.len() == 1 {
|
||||
pyarg_attrs.remove(0)
|
||||
} else {
|
||||
panic!(
|
||||
"Multiple pyarg attributes on field '{}'",
|
||||
field.ident.as_ref().unwrap()
|
||||
);
|
||||
};
|
||||
|
||||
let name = &field.ident;
|
||||
let middle = quote! {
|
||||
.map(|x| ::rustpython_vm::pyobject::TryFromObject::try_from_object(vm, x)).transpose()?
|
||||
};
|
||||
let ending = if let Some(default) = attr.default {
|
||||
quote! {
|
||||
.unwrap_or_else(|| #default)
|
||||
}
|
||||
} else if attr.optional {
|
||||
quote! {
|
||||
.map(::rustpython_vm::function::OptionalArg::Present)
|
||||
.unwrap_or(::rustpython_vm::function::OptionalArg::Missing)
|
||||
}
|
||||
} else {
|
||||
let err = match attr.kind {
|
||||
ParameterKind::PositionalOnly | ParameterKind::PositionalOrKeyword => quote! {
|
||||
::rustpython_vm::function::ArgumentError::TooFewArgs
|
||||
},
|
||||
ParameterKind::KeywordOnly => quote! {
|
||||
::rustpython_vm::function::ArgumentError::RequiredKeywordArgument(tringify!(#name))
|
||||
},
|
||||
};
|
||||
quote! {
|
||||
.ok_or_else(|| #err)?
|
||||
}
|
||||
};
|
||||
|
||||
match attr.kind {
|
||||
ParameterKind::PositionalOnly => {
|
||||
quote! {
|
||||
#name: args.take_positional()#middle#ending,
|
||||
}
|
||||
}
|
||||
ParameterKind::PositionalOrKeyword => {
|
||||
quote! {
|
||||
#name: args.take_positional_keyword(stringify!(#name))#middle#ending,
|
||||
}
|
||||
}
|
||||
ParameterKind::KeywordOnly => {
|
||||
quote! {
|
||||
#name: args.take_keyword(stringify!(#name))#middle#ending,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn impl_from_args(input: DeriveInput) -> TokenStream2 {
|
||||
let fields = match input.data {
|
||||
Data::Struct(ref data) => {
|
||||
match data.fields {
|
||||
Fields::Named(ref fields) => fields.named.iter().map(generate_field),
|
||||
Fields::Unnamed(_) | Fields::Unit => unimplemented!(), // TODO: better error message
|
||||
}
|
||||
}
|
||||
Data::Enum(_) | Data::Union(_) => unimplemented!(), // TODO: better error message
|
||||
};
|
||||
|
||||
let name = &input.ident;
|
||||
quote! {
|
||||
impl ::rustpython_vm::function::FromArgs for #name {
|
||||
fn from_args(
|
||||
vm: &::rustpython_vm::VirtualMachine,
|
||||
args: &mut ::rustpython_vm::function::PyFuncArgs
|
||||
) -> Result<Self, ::rustpython_vm::function::ArgumentError> {
|
||||
Ok(#name { #(#fields)* })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
-36
@@ -1,48 +1,49 @@
|
||||
#![recursion_limit = "128"]
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
#[macro_use]
|
||||
mod error;
|
||||
mod from_args;
|
||||
mod pyclass;
|
||||
|
||||
use error::Diagnostic;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use syn::{Data, DeriveInput, Fields};
|
||||
use quote::ToTokens;
|
||||
use syn::{parse_macro_input, AttributeArgs, DeriveInput, Item};
|
||||
|
||||
#[proc_macro_derive(FromArgs)]
|
||||
fn result_to_tokens(result: Result<TokenStream2, Diagnostic>) -> TokenStream {
|
||||
match result {
|
||||
Ok(tokens) => tokens.into(),
|
||||
Err(diagnostic) => diagnostic.into_token_stream().into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromArgs, attributes(pyarg))]
|
||||
pub fn derive_from_args(input: TokenStream) -> TokenStream {
|
||||
let ast: DeriveInput = syn::parse(input).unwrap();
|
||||
|
||||
let gen = impl_from_args(&ast);
|
||||
gen.to_string().parse().unwrap()
|
||||
from_args::impl_from_args(ast).into()
|
||||
}
|
||||
|
||||
fn impl_from_args(input: &DeriveInput) -> TokenStream2 {
|
||||
// FIXME: This references types using `crate` instead of `rustpython_vm`
|
||||
// so that it can be used in the latter. How can we support both?
|
||||
let fields = match input.data {
|
||||
Data::Struct(ref data) => {
|
||||
match data.fields {
|
||||
Fields::Named(ref fields) => fields.named.iter().map(|field| {
|
||||
let name = &field.ident;
|
||||
quote! {
|
||||
#name: crate::pyobject::TryFromObject::try_from_object(
|
||||
vm,
|
||||
args.take_keyword(stringify!(#name)).unwrap_or_else(|| vm.ctx.none())
|
||||
)?,
|
||||
}
|
||||
}),
|
||||
Fields::Unnamed(_) | Fields::Unit => unimplemented!(), // TODO: better error message
|
||||
}
|
||||
}
|
||||
Data::Enum(_) | Data::Union(_) => unimplemented!(), // TODO: better error message
|
||||
};
|
||||
#[proc_macro_attribute]
|
||||
pub fn pyclass(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let attr = parse_macro_input!(attr as AttributeArgs);
|
||||
let item = parse_macro_input!(item as Item);
|
||||
result_to_tokens(pyclass::impl_pyclass(attr, item))
|
||||
}
|
||||
|
||||
let name = &input.ident;
|
||||
quote! {
|
||||
impl crate::function::FromArgs for #name {
|
||||
fn from_args(
|
||||
vm: &crate::vm::VirtualMachine,
|
||||
args: &mut crate::function::PyFuncArgs
|
||||
) -> Result<Self, crate::function::ArgumentError> {
|
||||
Ok(#name { #(#fields)* })
|
||||
}
|
||||
}
|
||||
}
|
||||
#[proc_macro_attribute]
|
||||
pub fn pyimpl(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let attr = parse_macro_input!(attr as AttributeArgs);
|
||||
let item = parse_macro_input!(item as Item);
|
||||
result_to_tokens(pyclass::impl_pyimpl(attr, item))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn pystruct_sequence(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let attr = parse_macro_input!(attr as AttributeArgs);
|
||||
let item = parse_macro_input!(item as Item);
|
||||
result_to_tokens(pyclass::impl_pystruct_sequence(attr, item))
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
use super::Diagnostic;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use std::collections::HashMap;
|
||||
use syn::{
|
||||
Attribute, AttributeArgs, Ident, ImplItem, Index, Item, Lit, Meta, MethodSig, NestedMeta,
|
||||
};
|
||||
|
||||
enum ClassItem {
|
||||
Method {
|
||||
item_ident: Ident,
|
||||
py_name: String,
|
||||
},
|
||||
Property {
|
||||
item_ident: Ident,
|
||||
py_name: String,
|
||||
setter: bool,
|
||||
},
|
||||
}
|
||||
|
||||
fn meta_to_vec(meta: Meta) -> Result<Vec<NestedMeta>, Meta> {
|
||||
match meta {
|
||||
Meta::Word(_) => Ok(Vec::new()),
|
||||
Meta::List(list) => Ok(list.nested.into_iter().collect()),
|
||||
Meta::NameValue(_) => Err(meta),
|
||||
}
|
||||
}
|
||||
|
||||
impl ClassItem {
|
||||
fn extract_from_syn(
|
||||
attrs: &mut Vec<Attribute>,
|
||||
sig: &MethodSig,
|
||||
) -> Result<Option<ClassItem>, Diagnostic> {
|
||||
let mut item = None;
|
||||
let mut attr_idx = None;
|
||||
for (i, meta) in attrs
|
||||
.iter()
|
||||
.filter_map(|attr| attr.parse_meta().ok())
|
||||
.enumerate()
|
||||
{
|
||||
let name = meta.name();
|
||||
if name == "pymethod" {
|
||||
if item.is_some() {
|
||||
bail_span!(
|
||||
sig.ident,
|
||||
"You can only have one #[py*] attribute on an impl item"
|
||||
)
|
||||
}
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| {
|
||||
err_span!(
|
||||
meta,
|
||||
"#[pyproperty = \"...\"] cannot be a name/value, you probably meant \
|
||||
#[pyproperty(name = \"...\")]",
|
||||
)
|
||||
})?;
|
||||
let mut py_name = None;
|
||||
for meta in nesteds {
|
||||
let meta = match meta {
|
||||
NestedMeta::Meta(meta) => meta,
|
||||
NestedMeta::Literal(_) => continue,
|
||||
};
|
||||
match meta {
|
||||
Meta::NameValue(name_value) => {
|
||||
if name_value.ident == "name" {
|
||||
if let Lit::Str(s) = &name_value.lit {
|
||||
py_name = Some(s.value());
|
||||
} else {
|
||||
bail_span!(
|
||||
&sig.ident,
|
||||
"#[pymethod(name = ...)] must be a string"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
item = Some(ClassItem::Method {
|
||||
item_ident: sig.ident.clone(),
|
||||
py_name: py_name.unwrap_or_else(|| sig.ident.to_string()),
|
||||
});
|
||||
attr_idx = Some(i);
|
||||
} else if name == "pyproperty" {
|
||||
if item.is_some() {
|
||||
bail_span!(
|
||||
sig.ident,
|
||||
"You can only have one #[py*] attribute on an impl item"
|
||||
)
|
||||
}
|
||||
let nesteds = meta_to_vec(meta).map_err(|meta| {
|
||||
err_span!(
|
||||
meta,
|
||||
"#[pyproperty = \"...\"] cannot be a name/value, you probably meant \
|
||||
#[pyproperty(name = \"...\")]"
|
||||
)
|
||||
})?;
|
||||
let mut setter = false;
|
||||
let mut py_name = None;
|
||||
for meta in nesteds {
|
||||
let meta = match meta {
|
||||
NestedMeta::Meta(meta) => meta,
|
||||
NestedMeta::Literal(_) => continue,
|
||||
};
|
||||
match meta {
|
||||
Meta::NameValue(name_value) => {
|
||||
if name_value.ident == "name" {
|
||||
if let Lit::Str(s) = &name_value.lit {
|
||||
py_name = Some(s.value());
|
||||
} else {
|
||||
bail_span!(
|
||||
&sig.ident,
|
||||
"#[pyproperty(name = ...)] must be a string"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Meta::Word(ident) => {
|
||||
if ident == "setter" {
|
||||
setter = true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let py_name = match py_name {
|
||||
Some(py_name) => py_name,
|
||||
None => {
|
||||
let item_ident = sig.ident.to_string();
|
||||
if setter {
|
||||
if item_ident.starts_with("set_") {
|
||||
let name = &item_ident["set_".len()..];
|
||||
if name.is_empty() {
|
||||
bail_span!(
|
||||
&sig.ident,
|
||||
"A #[pyproperty(setter)] fn with a set_* name must \
|
||||
have something after \"set_\""
|
||||
)
|
||||
} else {
|
||||
name.to_string()
|
||||
}
|
||||
} else {
|
||||
bail_span!(
|
||||
&sig.ident,
|
||||
"A #[pyproperty(setter)] fn must either have a `name` \
|
||||
parameter or a fn name along the lines of \"set_*\""
|
||||
)
|
||||
}
|
||||
} else {
|
||||
item_ident
|
||||
}
|
||||
}
|
||||
};
|
||||
item = Some(ClassItem::Property {
|
||||
py_name,
|
||||
item_ident: sig.ident.clone(),
|
||||
setter,
|
||||
});
|
||||
attr_idx = Some(i);
|
||||
}
|
||||
}
|
||||
if let Some(attr_idx) = attr_idx {
|
||||
attrs.remove(attr_idx);
|
||||
}
|
||||
Ok(item)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn impl_pyimpl(_attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
|
||||
let mut imp = if let Item::Impl(imp) = item {
|
||||
imp
|
||||
} else {
|
||||
return Ok(quote!(#item));
|
||||
};
|
||||
|
||||
let mut diagnostics: Vec<Diagnostic> = Vec::new();
|
||||
|
||||
let items = imp
|
||||
.items
|
||||
.iter_mut()
|
||||
.filter_map(|item| {
|
||||
if let ImplItem::Method(meth) = item {
|
||||
ClassItem::extract_from_syn(&mut meth.attrs, &meth.sig)
|
||||
.map_err(|err| diagnostics.push(err))
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let ty = &imp.self_ty;
|
||||
let mut properties: HashMap<&str, (Option<&Ident>, Option<&Ident>)> = HashMap::new();
|
||||
for item in items.iter() {
|
||||
match item {
|
||||
ClassItem::Property {
|
||||
ref item_ident,
|
||||
ref py_name,
|
||||
setter,
|
||||
} => {
|
||||
let entry = properties.entry(py_name).or_default();
|
||||
let func = if *setter { &mut entry.1 } else { &mut entry.0 };
|
||||
if func.is_some() {
|
||||
bail_span!(
|
||||
item_ident,
|
||||
"Multiple property accessors with name {:?}",
|
||||
py_name
|
||||
)
|
||||
}
|
||||
*func = Some(item_ident);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let methods = items.iter().filter_map(|item| {
|
||||
if let ClassItem::Method {
|
||||
item_ident,
|
||||
py_name,
|
||||
} = item
|
||||
{
|
||||
Some(quote! {
|
||||
class.set_str_attr(#py_name, ctx.new_rustfunc(Self::#item_ident));
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let properties = properties
|
||||
.iter()
|
||||
.map(|(name, prop)| {
|
||||
let getter = match prop.0 {
|
||||
Some(getter) => getter,
|
||||
None => {
|
||||
push_err_span!(
|
||||
diagnostics,
|
||||
prop.1.unwrap(),
|
||||
"Property {:?} is missing a getter",
|
||||
name
|
||||
);
|
||||
return TokenStream2::new();
|
||||
}
|
||||
};
|
||||
let add_setter = prop.1.map(|setter| quote!(.add_setter(Self::#setter)));
|
||||
quote! {
|
||||
class.set_str_attr(
|
||||
#name,
|
||||
::rustpython_vm::obj::objproperty::PropertyBuilder::new(ctx)
|
||||
.add_getter(Self::#getter)
|
||||
#add_setter
|
||||
.create(),
|
||||
);
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Diagnostic::from_vec(diagnostics)?;
|
||||
|
||||
let ret = quote! {
|
||||
#imp
|
||||
impl ::rustpython_vm::pyobject::PyClassImpl for #ty {
|
||||
fn impl_extend_class(
|
||||
ctx: &::rustpython_vm::pyobject::PyContext,
|
||||
class: &::rustpython_vm::obj::objtype::PyClassRef,
|
||||
) {
|
||||
#(#methods)*
|
||||
#(#properties)*
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn generate_class_def(
|
||||
ident: &Ident,
|
||||
attr_name: &'static str,
|
||||
attr: AttributeArgs,
|
||||
attrs: &Vec<Attribute>,
|
||||
) -> Result<TokenStream2, Diagnostic> {
|
||||
let mut class_name = None;
|
||||
for attr in attr {
|
||||
if let NestedMeta::Meta(meta) = attr {
|
||||
if let Meta::NameValue(name_value) = meta {
|
||||
if name_value.ident == "name" {
|
||||
if let Lit::Str(s) = name_value.lit {
|
||||
class_name = Some(s.value());
|
||||
} else {
|
||||
bail_span!(
|
||||
name_value.lit,
|
||||
"#[{}(name = ...)] must be a string",
|
||||
attr_name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let class_name = class_name.unwrap_or_else(|| ident.to_string());
|
||||
|
||||
let mut doc: Option<Vec<String>> = None;
|
||||
for attr in attrs.iter() {
|
||||
if attr.path.is_ident("doc") {
|
||||
let meta = attr.parse_meta().expect("expected doc attr to be a meta");
|
||||
if let Meta::NameValue(name_value) = meta {
|
||||
if let Lit::Str(s) = name_value.lit {
|
||||
let val = s.value().trim().to_string();
|
||||
match doc {
|
||||
Some(ref mut doc) => doc.push(val),
|
||||
None => doc = Some(vec![val]),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let doc = match doc {
|
||||
Some(doc) => {
|
||||
let doc = doc.join("\n");
|
||||
quote!(Some(#doc))
|
||||
}
|
||||
None => quote!(None),
|
||||
};
|
||||
|
||||
let ret = quote! {
|
||||
impl ::rustpython_vm::pyobject::PyClassDef for #ident {
|
||||
const NAME: &'static str = #class_name;
|
||||
const DOC: Option<&'static str> = #doc;
|
||||
}
|
||||
};
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
|
||||
let (item, ident, attrs) = match item {
|
||||
Item::Struct(struc) => (quote!(#struc), struc.ident, struc.attrs),
|
||||
Item::Enum(enu) => (quote!(#enu), enu.ident, enu.attrs),
|
||||
other => bail_span!(
|
||||
other,
|
||||
"#[pyclass] can only be on a struct or enum declaration"
|
||||
),
|
||||
};
|
||||
|
||||
let class_def = generate_class_def(&ident, "pyclass", attr, &attrs)?;
|
||||
|
||||
let ret = quote! {
|
||||
#item
|
||||
#class_def
|
||||
};
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn impl_pystruct_sequence(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
|
||||
let struc = if let Item::Struct(struc) = item {
|
||||
struc
|
||||
} else {
|
||||
bail_span!(
|
||||
item,
|
||||
"#[pystruct_sequence] can only be on a struct declaration"
|
||||
)
|
||||
};
|
||||
let class_def = generate_class_def(&struc.ident, "pystruct_sequence", attr, &struc.attrs)?;
|
||||
let mut properties = Vec::new();
|
||||
let mut field_names = Vec::new();
|
||||
for (i, field) in struc.fields.iter().enumerate() {
|
||||
let idx = Index::from(i);
|
||||
if let Some(ref field_name) = field.ident {
|
||||
let field_name_str = field_name.to_string();
|
||||
// TODO add doc to the generated property
|
||||
let property = quote! {
|
||||
class.set_str_attr(
|
||||
#field_name_str,
|
||||
::rustpython_vm::obj::objproperty::PropertyBuilder::new(ctx)
|
||||
.add_getter(|zelf: &::rustpython_vm::obj::objtuple::PyTuple,
|
||||
_vm: &::rustpython_vm::vm::VirtualMachine|
|
||||
zelf.fast_getitem(#idx))
|
||||
.create(),
|
||||
);
|
||||
};
|
||||
properties.push(property);
|
||||
field_names.push(quote!(#field_name));
|
||||
} else {
|
||||
field_names.push(quote!(#idx));
|
||||
}
|
||||
}
|
||||
|
||||
let ty = &struc.ident;
|
||||
let ret = quote! {
|
||||
#struc
|
||||
#class_def
|
||||
impl #ty {
|
||||
fn into_struct_sequence(&self,
|
||||
vm: &::rustpython_vm::vm::VirtualMachine,
|
||||
cls: ::rustpython_vm::obj::objtype::PyClassRef,
|
||||
) -> ::rustpython_vm::pyobject::PyResult<::rustpython_vm::obj::objtuple::PyTupleRef> {
|
||||
let tuple: ::rustpython_vm::obj::objtuple::PyTuple =
|
||||
vec![#(::rustpython_vm::pyobject::IntoPyObject
|
||||
::into_pyobject(self.#field_names, vm)?
|
||||
),*].into();
|
||||
::rustpython_vm::pyobject::PyValue::into_ref_with_type(tuple, vm, cls)
|
||||
}
|
||||
}
|
||||
impl ::rustpython_vm::pyobject::PyClassImpl for #ty {
|
||||
fn impl_extend_class(
|
||||
ctx: &::rustpython_vm::pyobject::PyContext,
|
||||
class: &::rustpython_vm::obj::objtype::PyClassRef,
|
||||
) {
|
||||
#(#properties)*
|
||||
}
|
||||
|
||||
fn make_class(
|
||||
ctx: &::rustpython_vm::pyobject::PyContext
|
||||
) -> ::rustpython_vm::obj::objtype::PyClassRef {
|
||||
let py_class = ctx.new_class(<Self as ::rustpython_vm::pyobject::PyClassDef>::NAME, ctx.tuple_type());
|
||||
Self::extend_class(ctx, &py_class);
|
||||
py_class
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(ret)
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
src/python.rs
|
||||
target/
|
||||
Cargo.lock
|
||||
|
||||
+5
-3
@@ -6,12 +6,14 @@ build = "build.rs"
|
||||
edition = "2018"
|
||||
|
||||
[build-dependencies]
|
||||
lalrpop="0.15.1"
|
||||
lalrpop="0.16.3"
|
||||
|
||||
[dependencies]
|
||||
lalrpop-util="0.15.1"
|
||||
lalrpop-util="0.16.3"
|
||||
log="0.4.1"
|
||||
regex="0.2.2"
|
||||
num-bigint = "0.2"
|
||||
num-traits = "0.2"
|
||||
|
||||
unicode-xid = "0.1.0"
|
||||
unic-emoji-char = "0.9.0"
|
||||
serde = { version = "1.0.66", features = ["derive"] }
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use lalrpop;
|
||||
|
||||
fn main() {
|
||||
lalrpop::process_root().unwrap();
|
||||
lalrpop::process_root().unwrap()
|
||||
}
|
||||
+27
-10
@@ -4,6 +4,8 @@
|
||||
|
||||
pub use super::lexer::Location;
|
||||
use num_bigint::BigInt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/*
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -16,7 +18,7 @@ pub struct Node {
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Top {
|
||||
Program(Program),
|
||||
Statement(LocatedStatement),
|
||||
Statement(Vec<LocatedStatement>),
|
||||
Expression(Expression),
|
||||
}
|
||||
|
||||
@@ -42,12 +44,13 @@ pub struct Located<T> {
|
||||
pub type LocatedStatement = Located<Statement>;
|
||||
|
||||
/// Abstract syntax tree nodes for python statements.
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Statement {
|
||||
Break,
|
||||
Continue,
|
||||
Return {
|
||||
value: Option<Vec<Expression>>,
|
||||
value: Option<Box<Expression>>,
|
||||
},
|
||||
Import {
|
||||
import_parts: Vec<SingleImport>,
|
||||
@@ -94,7 +97,13 @@ pub enum Statement {
|
||||
},
|
||||
For {
|
||||
target: Expression,
|
||||
iter: Vec<Expression>,
|
||||
iter: Expression,
|
||||
body: Vec<LocatedStatement>,
|
||||
orelse: Option<Vec<LocatedStatement>>,
|
||||
},
|
||||
AsyncFor {
|
||||
target: Expression,
|
||||
iter: Expression,
|
||||
body: Vec<LocatedStatement>,
|
||||
orelse: Option<Vec<LocatedStatement>>,
|
||||
},
|
||||
@@ -114,12 +123,17 @@ pub enum Statement {
|
||||
bases: Vec<Expression>,
|
||||
keywords: Vec<Keyword>,
|
||||
decorator_list: Vec<Expression>,
|
||||
// TODO: docstring: String,
|
||||
},
|
||||
FunctionDef {
|
||||
name: String,
|
||||
args: Parameters,
|
||||
// docstring: String,
|
||||
body: Vec<LocatedStatement>,
|
||||
decorator_list: Vec<Expression>,
|
||||
returns: Option<Expression>,
|
||||
},
|
||||
AsyncFunctionDef {
|
||||
name: String,
|
||||
args: Parameters,
|
||||
body: Vec<LocatedStatement>,
|
||||
decorator_list: Vec<Expression>,
|
||||
returns: Option<Expression>,
|
||||
@@ -152,6 +166,9 @@ pub enum Expression {
|
||||
op: UnaryOperator,
|
||||
a: Box<Expression>,
|
||||
},
|
||||
Await {
|
||||
value: Box<Expression>,
|
||||
},
|
||||
Yield {
|
||||
value: Option<Box<Expression>>,
|
||||
},
|
||||
@@ -159,9 +176,8 @@ pub enum Expression {
|
||||
value: Box<Expression>,
|
||||
},
|
||||
Compare {
|
||||
a: Box<Expression>,
|
||||
op: Comparison,
|
||||
b: Box<Expression>,
|
||||
vals: Vec<Expression>,
|
||||
ops: Vec<Comparison>,
|
||||
},
|
||||
Attribute {
|
||||
value: Box<Expression>,
|
||||
@@ -182,7 +198,7 @@ pub enum Expression {
|
||||
elements: Vec<Expression>,
|
||||
},
|
||||
Dict {
|
||||
elements: Vec<(Expression, Expression)>,
|
||||
elements: Vec<(Option<Expression>, Expression)>,
|
||||
},
|
||||
Set {
|
||||
elements: Vec<Expression>,
|
||||
@@ -230,6 +246,7 @@ impl Expression {
|
||||
match self {
|
||||
BoolOp { .. } | Binop { .. } | Unop { .. } => "operator",
|
||||
Subscript { .. } => "subscript",
|
||||
Await { .. } => "await expression",
|
||||
Yield { .. } | YieldFrom { .. } => "yield expression",
|
||||
Compare { .. } => "comparison",
|
||||
Attribute { .. } => "attribute",
|
||||
@@ -367,7 +384,7 @@ pub enum Number {
|
||||
}
|
||||
|
||||
/// Transforms a value prior to formatting it.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ConversionFlag {
|
||||
/// Converts by calling `str(<value>)`.
|
||||
Str,
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::str;
|
||||
use lalrpop_util::ParseError as LalrpopError;
|
||||
|
||||
use crate::ast::{ConversionFlag, StringGroup};
|
||||
use crate::lexer::{LexicalError, Location, Tok};
|
||||
use crate::lexer::{LexicalError, LexicalErrorType, Location, Tok};
|
||||
use crate::parser::parse_expression;
|
||||
|
||||
use self::FStringError::*;
|
||||
@@ -25,7 +25,10 @@ pub enum FStringError {
|
||||
impl From<FStringError> for LalrpopError<Location, Tok, LexicalError> {
|
||||
fn from(_err: FStringError) -> Self {
|
||||
lalrpop_util::ParseError::User {
|
||||
error: LexicalError::StringError,
|
||||
error: LexicalError {
|
||||
error: LexicalErrorType::StringError,
|
||||
location: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+608
-378
File diff suppressed because it is too large.
Load diff
+5
-2
@@ -1,11 +1,14 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
use lalrpop_util::lalrpop_mod;
|
||||
|
||||
pub mod ast;
|
||||
pub mod error;
|
||||
mod fstring;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
mod python;
|
||||
lalrpop_mod!(
|
||||
#[allow(clippy::all)]
|
||||
python
|
||||
);
|
||||
pub mod token;
|
||||
+29
-25
@@ -35,7 +35,7 @@ pub fn parse_program(source: &str) -> Result<ast::Program, ParseError> {
|
||||
do_lalr_parsing!(source, Program, StartProgram)
|
||||
}
|
||||
|
||||
pub fn parse_statement(source: &str) -> Result<ast::LocatedStatement, ParseError> {
|
||||
pub fn parse_statement(source: &str) -> Result<Vec<ast::LocatedStatement>, ParseError> {
|
||||
do_lalr_parsing!(source, Statement, StartStatement)
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ mod tests {
|
||||
let parse_ast = parse_statement(&source).unwrap();
|
||||
assert_eq!(
|
||||
parse_ast,
|
||||
ast::LocatedStatement {
|
||||
vec![ast::LocatedStatement {
|
||||
location: ast::Location::new(1, 1),
|
||||
node: ast::Statement::If {
|
||||
test: ast::Expression::Number {
|
||||
@@ -229,7 +229,7 @@ mod tests {
|
||||
}
|
||||
},]),
|
||||
}
|
||||
}
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ mod tests {
|
||||
let parse_ast = parse_statement(&source);
|
||||
assert_eq!(
|
||||
parse_ast,
|
||||
Ok(ast::LocatedStatement {
|
||||
Ok(vec![ast::LocatedStatement {
|
||||
location: ast::Location::new(1, 1),
|
||||
node: ast::Statement::Expression {
|
||||
expression: ast::Expression::Lambda {
|
||||
@@ -271,7 +271,7 @@ mod tests {
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}])
|
||||
)
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
parse_statement(&source),
|
||||
Ok(ast::LocatedStatement {
|
||||
Ok(vec![ast::LocatedStatement {
|
||||
location: ast::Location::new(1, 1),
|
||||
node: ast::Statement::Assign {
|
||||
targets: vec![ast::Expression::Tuple {
|
||||
@@ -309,7 +309,7 @@ mod tests {
|
||||
]
|
||||
}
|
||||
}
|
||||
})
|
||||
}])
|
||||
)
|
||||
}
|
||||
|
||||
@@ -320,7 +320,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
parse_statement(&source),
|
||||
Ok(ast::LocatedStatement {
|
||||
Ok(vec![ast::LocatedStatement {
|
||||
location: ast::Location::new(1, 1),
|
||||
node: ast::Statement::ClassDef {
|
||||
name: String::from("Foo"),
|
||||
@@ -393,7 +393,7 @@ mod tests {
|
||||
],
|
||||
decorator_list: vec![],
|
||||
}
|
||||
})
|
||||
}])
|
||||
)
|
||||
}
|
||||
|
||||
@@ -460,26 +460,30 @@ mod tests {
|
||||
},
|
||||
ifs: vec![
|
||||
ast::Expression::Compare {
|
||||
a: Box::new(ast::Expression::Identifier {
|
||||
name: "a".to_string()
|
||||
}),
|
||||
op: ast::Comparison::Less,
|
||||
b: Box::new(ast::Expression::Number {
|
||||
value: ast::Number::Integer {
|
||||
value: BigInt::from(5)
|
||||
vals: vec![
|
||||
ast::Expression::Identifier {
|
||||
name: "a".to_string()
|
||||
},
|
||||
ast::Expression::Number {
|
||||
value: ast::Number::Integer {
|
||||
value: BigInt::from(5)
|
||||
}
|
||||
}
|
||||
}),
|
||||
],
|
||||
ops: vec![ast::Comparison::Less],
|
||||
},
|
||||
ast::Expression::Compare {
|
||||
a: Box::new(ast::Expression::Identifier {
|
||||
name: "a".to_string()
|
||||
}),
|
||||
op: ast::Comparison::Greater,
|
||||
b: Box::new(ast::Expression::Number {
|
||||
value: ast::Number::Integer {
|
||||
value: BigInt::from(10)
|
||||
vals: vec![
|
||||
ast::Expression::Identifier {
|
||||
name: "a".to_string()
|
||||
},
|
||||
ast::Expression::Number {
|
||||
value: ast::Number::Integer {
|
||||
value: BigInt::from(10)
|
||||
}
|
||||
}
|
||||
}),
|
||||
],
|
||||
ops: vec![ast::Comparison::Greater],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
+174
-216
@@ -2,7 +2,6 @@
|
||||
// See also: https://github.com/antlr/grammars-v4/blob/master/python3/Python3.g4
|
||||
// See also: file:///usr/share/doc/python/html/reference/compound_stmts.html#function-definitions
|
||||
// See also: https://greentreesnakes.readthedocs.io/en/latest/nodes.html#keyword
|
||||
#![allow(unknown_lints,clippy)]
|
||||
|
||||
use std::iter::FromIterator;
|
||||
|
||||
@@ -25,29 +24,32 @@ pub Top: ast::Top = {
|
||||
|
||||
Program: ast::Program = {
|
||||
<lines:FileLine*> => ast::Program {
|
||||
statements: Vec::from_iter(lines.into_iter().filter_map(|e| e))
|
||||
statements: Vec::from_iter(lines.into_iter().flatten())
|
||||
},
|
||||
};
|
||||
|
||||
// A file line either has a declaration, or an empty newline:
|
||||
FileLine: Option<ast::LocatedStatement> = {
|
||||
<s:Statement> => Some(s),
|
||||
"\n" => None,
|
||||
FileLine: Vec<ast::LocatedStatement> = {
|
||||
Statement,
|
||||
"\n" => vec![],
|
||||
};
|
||||
|
||||
Suite: Vec<ast::LocatedStatement> = {
|
||||
<s:SimpleStatement> => vec![s],
|
||||
"\n" indent <s:Statement+> dedent => s,
|
||||
};
|
||||
|
||||
Statement: ast::LocatedStatement = {
|
||||
SimpleStatement,
|
||||
CompoundStatement,
|
||||
"\n" indent <s:Statement+> dedent => s.into_iter().flatten().collect(),
|
||||
};
|
||||
|
||||
SimpleStatement: ast::LocatedStatement = {
|
||||
<s:SmallStatement> "\n" => s,
|
||||
<s:SmallStatement> ";" => s,
|
||||
Statement: Vec<ast::LocatedStatement> = {
|
||||
SimpleStatement,
|
||||
<s:CompoundStatement> => vec![s],
|
||||
};
|
||||
|
||||
SimpleStatement: Vec<ast::LocatedStatement> = {
|
||||
<s1:SmallStatement> <s2:(";" SmallStatement)*> ";"? "\n" => {
|
||||
let mut statements = vec![s1];
|
||||
statements.extend(s2.into_iter().map(|e| e.1));
|
||||
statements
|
||||
}
|
||||
};
|
||||
|
||||
SmallStatement: ast::LocatedStatement = {
|
||||
@@ -103,9 +105,7 @@ ExpressionStatement: ast::LocatedStatement = {
|
||||
}
|
||||
}
|
||||
},
|
||||
<loc:@L> <expr:TestOrStarExprList> <op:AugAssign> <e2:TestList> => {
|
||||
// TODO: this works in most cases:
|
||||
let rhs = e2.into_iter().next().unwrap();
|
||||
<loc:@L> <expr:TestOrStarExprList> <op:AugAssign> <rhs:TestList> => {
|
||||
ast::LocatedStatement {
|
||||
location: loc,
|
||||
node: ast::Statement::AugAssign {
|
||||
@@ -118,31 +118,17 @@ ExpressionStatement: ast::LocatedStatement = {
|
||||
};
|
||||
|
||||
AssignSuffix: ast::Expression = {
|
||||
"=" <e:TestList> => {
|
||||
if e.len() > 1 {
|
||||
ast::Expression::Tuple {
|
||||
elements: e
|
||||
}
|
||||
} else {
|
||||
e.into_iter().next().unwrap()
|
||||
}
|
||||
},
|
||||
"=" <e:TestList> => e,
|
||||
"=" <e:YieldExpr> => e,
|
||||
};
|
||||
|
||||
TestOrStarExprList: ast::Expression = {
|
||||
<e:TestOrStarExpr> <e2:("," TestOrStarExpr)*> <comma:","?> => {
|
||||
let mut res = vec![e];
|
||||
res.extend(e2.into_iter().map(|x| x.1));
|
||||
|
||||
// First build tuple from first item:
|
||||
let expr = if (res.len() > 1) || comma.is_some() {
|
||||
ast::Expression::Tuple { elements: res }
|
||||
<elements:OneOrMore<TestOrStarExpr>> <comma:","?> => {
|
||||
if elements.len() == 1 && comma.is_none() {
|
||||
elements.into_iter().next().unwrap()
|
||||
} else {
|
||||
res.into_iter().next().unwrap()
|
||||
};
|
||||
|
||||
expr
|
||||
ast::Expression::Tuple { elements }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -183,7 +169,7 @@ FlowStatement: ast::LocatedStatement = {
|
||||
<loc:@L> "return" <t:TestList?> => {
|
||||
ast::LocatedStatement {
|
||||
location: loc,
|
||||
node: ast::Statement::Return { value: t },
|
||||
node: ast::Statement::Return { value: t.map(Box::new) },
|
||||
}
|
||||
},
|
||||
<loc:@L> <y:YieldExpr> => {
|
||||
@@ -310,15 +296,11 @@ NonlocalStatement: ast::LocatedStatement = {
|
||||
};
|
||||
|
||||
AssertStatement: ast::LocatedStatement = {
|
||||
<loc:@L> "assert" <t:Test> <m: ("," Test)?> => {
|
||||
<loc:@L> "assert" <test:Test> <msg: ("," Test)?> => {
|
||||
ast::LocatedStatement {
|
||||
location: loc,
|
||||
node: ast::Statement::Assert {
|
||||
test: t,
|
||||
msg: match m {
|
||||
Some(e) => Some(e.1),
|
||||
None => None,
|
||||
}
|
||||
test, msg: msg.map(|e| e.1)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -335,12 +317,9 @@ CompoundStatement: ast::LocatedStatement = {
|
||||
};
|
||||
|
||||
IfStatement: ast::LocatedStatement = {
|
||||
<loc:@L> "if" <t:Test> ":" <s1:Suite> <s2:(@L "elif" Test ":" Suite)*> <s3:("else" ":" Suite)?> => {
|
||||
<loc:@L> "if" <test:Test> ":" <s1:Suite> <s2:(@L "elif" Test ":" Suite)*> <s3:("else" ":" Suite)?> => {
|
||||
// Determine last else:
|
||||
let mut last = match s3 {
|
||||
Some(s) => Some(s.2),
|
||||
None => None,
|
||||
};
|
||||
let mut last = s3.map(|s| s.2);
|
||||
|
||||
// handle elif:
|
||||
for i in s2.into_iter().rev() {
|
||||
@@ -353,35 +332,30 @@ IfStatement: ast::LocatedStatement = {
|
||||
|
||||
ast::LocatedStatement {
|
||||
location: loc,
|
||||
node: ast::Statement::If { test: t, body: s1, orelse: last }
|
||||
node: ast::Statement::If { test, body: s1, orelse: last }
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
WhileStatement: ast::LocatedStatement = {
|
||||
<loc:@L> "while" <e:Test> ":" <s:Suite> <s2:("else" ":" Suite)?> => {
|
||||
let or_else = match s2 {
|
||||
Some(s) => Some(s.2),
|
||||
None => None,
|
||||
};
|
||||
<loc:@L> "while" <test:Test> ":" <body:Suite> <s2:("else" ":" Suite)?> => {
|
||||
let or_else = s2.map(|s| s.2);
|
||||
ast::LocatedStatement {
|
||||
location: loc,
|
||||
node: ast::Statement::While { test: e, body: s, orelse: or_else },
|
||||
node: ast::Statement::While { test, body, orelse: or_else },
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
ForStatement: ast::LocatedStatement = {
|
||||
<loc:@L> "for" <e:ExpressionList> "in" <t:TestList> ":" <s:Suite> <s2:("else" ":" Suite)?> => {
|
||||
let or_else = match s2 {
|
||||
Some(s) => Some(s.2),
|
||||
None => None,
|
||||
};
|
||||
<loc:@L> <is_async:"async"?> "for" <target:ExpressionList> "in" <iter:TestList> ":" <body:Suite> <s2:("else" ":" Suite)?> => {
|
||||
let orelse = s2.map(|s| s.2);
|
||||
ast::LocatedStatement {
|
||||
location: loc,
|
||||
node: ast::Statement::For {
|
||||
target: e,
|
||||
iter: t, body: s, orelse: or_else
|
||||
node: if is_async.is_some() {
|
||||
ast::Statement::AsyncFor { target, iter, body, orelse }
|
||||
} else {
|
||||
ast::Statement::For { target, iter, body, orelse }
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -389,14 +363,8 @@ ForStatement: ast::LocatedStatement = {
|
||||
|
||||
TryStatement: ast::LocatedStatement = {
|
||||
<loc:@L> "try" ":" <body:Suite> <handlers:ExceptClause*> <else_suite:("else" ":" Suite)?> <finally:("finally" ":" Suite)?> => {
|
||||
let or_else = match else_suite {
|
||||
Some(s) => Some(s.2),
|
||||
None => None,
|
||||
};
|
||||
let finalbody = match finally {
|
||||
Some(s) => Some(s.2),
|
||||
None => None,
|
||||
};
|
||||
let or_else = else_suite.map(|s| s.2);
|
||||
let finalbody = finally.map(|s| s.2);
|
||||
ast::LocatedStatement {
|
||||
location: loc,
|
||||
node: ast::Statement::Try {
|
||||
@@ -437,51 +405,48 @@ WithStatement: ast::LocatedStatement = {
|
||||
|
||||
WithItem: ast::WithItem = {
|
||||
<t:Test> <n:("as" Expression)?> => {
|
||||
let optional_vars = match n {
|
||||
Some(val) => Some(val.1),
|
||||
None => None,
|
||||
};
|
||||
let optional_vars = n.map(|val| val.1);
|
||||
ast::WithItem { context_expr: t, optional_vars }
|
||||
},
|
||||
};
|
||||
|
||||
FuncDef: ast::LocatedStatement = {
|
||||
<d:Decorator*> <loc:@L> "def" <i:Identifier> <a:Parameters> <r:("->" Test)?> ":" <s:Suite> => {
|
||||
<d:Decorator*> <loc:@L> <is_async:"async"?> "def" <i:Identifier> <a:Parameters> <r:("->" Test)?> ":" <s:Suite> => {
|
||||
ast::LocatedStatement {
|
||||
location: loc,
|
||||
node: ast::Statement::FunctionDef {
|
||||
name: i,
|
||||
args: a,
|
||||
body: s,
|
||||
decorator_list: d,
|
||||
returns: r.map(|x| x.1),
|
||||
}
|
||||
node: if is_async.is_some() {
|
||||
ast::Statement::AsyncFunctionDef {
|
||||
name: i,
|
||||
args: a,
|
||||
body: s,
|
||||
decorator_list: d,
|
||||
returns: r.map(|x| x.1),
|
||||
}
|
||||
} else {
|
||||
ast::Statement::FunctionDef {
|
||||
name: i,
|
||||
args: a,
|
||||
body: s,
|
||||
decorator_list: d,
|
||||
returns: r.map(|x| x.1),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Parameters: ast::Parameters = {
|
||||
"(" <a: (TypedArgsList<TypedParameter>)?> ")" => {
|
||||
match a {
|
||||
Some(a) => a,
|
||||
None => Default::default(),
|
||||
}
|
||||
},
|
||||
"(" <a: (ParameterList<TypedParameter>)?> ")" => a.unwrap_or_else(Default::default),
|
||||
};
|
||||
|
||||
// parameters are (String, None), kwargs are (String, Some(Test)) where Test is
|
||||
// the default
|
||||
// Note that this is a macro which is used once for function defs, and
|
||||
// once for lambda defs.
|
||||
TypedArgsList<ArgType>: ast::Parameters = {
|
||||
<param1:TypedParameters<ArgType>> <args2:("," ParameterListStarArgs<ArgType>)?> => {
|
||||
ParameterList<ArgType>: ast::Parameters = {
|
||||
<param1:ParameterDefs<ArgType>> <args2:("," ParameterListStarArgs<ArgType>)?> ","? => {
|
||||
let (names, default_elements) = param1;
|
||||
|
||||
// Now gather rest of parameters:
|
||||
let (vararg, kwonlyargs, kw_defaults, kwarg) = match args2 {
|
||||
Some((_, x)) => x,
|
||||
None => (None, vec![], vec![], None),
|
||||
};
|
||||
let (vararg, kwonlyargs, kw_defaults, kwarg) = args2.map_or((None, vec![], vec![], None), |x| x.1);
|
||||
|
||||
ast::Parameters {
|
||||
args: names,
|
||||
@@ -492,7 +457,7 @@ TypedArgsList<ArgType>: ast::Parameters = {
|
||||
kw_defaults: kw_defaults,
|
||||
}
|
||||
},
|
||||
<param1:TypedParameters<ArgType>> <kw:("," KwargParameter<ArgType>)> => {
|
||||
<param1:ParameterDefs<ArgType>> <kw:("," KwargParameter<ArgType>)> ","? => {
|
||||
let (names, default_elements) = param1;
|
||||
|
||||
// Now gather rest of parameters:
|
||||
@@ -510,7 +475,7 @@ TypedArgsList<ArgType>: ast::Parameters = {
|
||||
kw_defaults: kw_defaults,
|
||||
}
|
||||
},
|
||||
<params:ParameterListStarArgs<ArgType>> => {
|
||||
<params:ParameterListStarArgs<ArgType>> ","? => {
|
||||
let (vararg, kwonlyargs, kw_defaults, kwarg) = params;
|
||||
ast::Parameters {
|
||||
args: vec![],
|
||||
@@ -521,7 +486,7 @@ TypedArgsList<ArgType>: ast::Parameters = {
|
||||
kw_defaults: kw_defaults,
|
||||
}
|
||||
},
|
||||
<kw:KwargParameter<ArgType>> => {
|
||||
<kw:KwargParameter<ArgType>> ","? => {
|
||||
ast::Parameters {
|
||||
args: vec![],
|
||||
kwonlyargs: vec![],
|
||||
@@ -535,8 +500,8 @@ TypedArgsList<ArgType>: ast::Parameters = {
|
||||
|
||||
// Use inline here to make sure the "," is not creating an ambiguity.
|
||||
#[inline]
|
||||
TypedParameters<ArgType>: (Vec<ast::Parameter>, Vec<ast::Expression>) = {
|
||||
<param1:TypedParameterDef<ArgType>> <param2:("," TypedParameterDef<ArgType>)*> => {
|
||||
ParameterDefs<ArgType>: (Vec<ast::Parameter>, Vec<ast::Expression>) = {
|
||||
<param1:ParameterDef<ArgType>> <param2:("," ParameterDef<ArgType>)*> => {
|
||||
// Combine first parameters:
|
||||
let mut args = vec![param1];
|
||||
args.extend(param2.into_iter().map(|x| x.1));
|
||||
@@ -564,7 +529,7 @@ TypedParameters<ArgType>: (Vec<ast::Parameter>, Vec<ast::Expression>) = {
|
||||
}
|
||||
};
|
||||
|
||||
TypedParameterDef<ArgType>: (ast::Parameter, Option<ast::Expression>) = {
|
||||
ParameterDef<ArgType>: (ast::Parameter, Option<ast::Expression>) = {
|
||||
<i:ArgType> => (i, None),
|
||||
<i:ArgType> "=" <e:Test> => (i, Some(e)),
|
||||
};
|
||||
@@ -580,8 +545,11 @@ TypedParameter: ast::Parameter = {
|
||||
},
|
||||
};
|
||||
|
||||
// Use inline here to make sure the "," is not creating an ambiguity.
|
||||
// TODO: figure out another grammar that makes this inline no longer required.
|
||||
#[inline]
|
||||
ParameterListStarArgs<ArgType>: (Option<Option<ast::Parameter>>, Vec<ast::Parameter>, Vec<Option<ast::Expression>>, Option<Option<ast::Parameter>>) = {
|
||||
"*" <va:ArgType?> <kw:("," TypedParameterDef<ArgType>)*> <kwarg:("," KwargParameter<ArgType>)?> => {
|
||||
"*" <va:ArgType?> <kw:("," ParameterDef<ArgType>)*> <kwarg:("," KwargParameter<ArgType>)?> => {
|
||||
// Extract keyword arguments:
|
||||
let mut kwonlyargs = vec![];
|
||||
let mut kw_defaults = vec![];
|
||||
@@ -590,10 +558,7 @@ ParameterListStarArgs<ArgType>: (Option<Option<ast::Parameter>>, Vec<ast::Parame
|
||||
kw_defaults.push(value);
|
||||
}
|
||||
|
||||
let kwarg = match kwarg {
|
||||
Some((_, name)) => Some(name),
|
||||
None => None,
|
||||
};
|
||||
let kwarg = kwarg.map(|n| n.1);
|
||||
|
||||
(Some(va), kwonlyargs, kw_defaults, kwarg)
|
||||
}
|
||||
@@ -649,66 +614,59 @@ Decorator: ast::Expression = {
|
||||
};
|
||||
|
||||
YieldExpr: ast::Expression = {
|
||||
"yield" <ex:TestList?> => {
|
||||
ast::Expression::Yield {
|
||||
value: ex.map(|expr| Box::new(
|
||||
if expr.len() > 1 {
|
||||
ast::Expression::Tuple { elements: expr }
|
||||
} else {
|
||||
expr.into_iter().next().unwrap()
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
"yield" "from" <e:Test> => {
|
||||
ast::Expression::YieldFrom {
|
||||
value: Box::new(e),
|
||||
}
|
||||
},
|
||||
"yield" <value:TestList?> => ast::Expression::Yield { value: value.map(Box::new) },
|
||||
"yield" "from" <e:Test> => ast::Expression::YieldFrom { value: Box::new(e) },
|
||||
};
|
||||
|
||||
Test: ast::Expression = {
|
||||
<e:OrTest> <c: ("if" OrTest "else" Test)?> => {
|
||||
match c {
|
||||
Some(c) => {
|
||||
ast::Expression::IfExpression {
|
||||
test: Box::new(c.1),
|
||||
body: Box::new(e),
|
||||
orelse: Box::new(c.3),
|
||||
}
|
||||
},
|
||||
None => e,
|
||||
<expr:OrTest> <condition: ("if" OrTest "else" Test)?> => {
|
||||
if let Some(c) = condition {
|
||||
ast::Expression::IfExpression {
|
||||
test: Box::new(c.1),
|
||||
body: Box::new(expr),
|
||||
orelse: Box::new(c.3),
|
||||
}
|
||||
} else {
|
||||
expr
|
||||
}
|
||||
},
|
||||
<e:LambdaDef> => e,
|
||||
LambdaDef,
|
||||
};
|
||||
|
||||
LambdaDef: ast::Expression = {
|
||||
"lambda" <p:TypedArgsList<UntypedParameter>?> ":" <b:Test> =>
|
||||
"lambda" <p:ParameterList<UntypedParameter>?> ":" <body:Test> =>
|
||||
ast::Expression::Lambda {
|
||||
args: p.unwrap_or(Default::default()),
|
||||
body:Box::new(b)
|
||||
body: Box::new(body)
|
||||
}
|
||||
}
|
||||
|
||||
OrTest: ast::Expression = {
|
||||
<e:AndTest> => e,
|
||||
AndTest,
|
||||
<e1:OrTest> "or" <e2:AndTest> => ast::Expression::BoolOp { a: Box::new(e1), op: ast::BooleanOperator::Or, b: Box::new(e2) },
|
||||
};
|
||||
|
||||
AndTest: ast::Expression = {
|
||||
<e:NotTest> => e,
|
||||
NotTest,
|
||||
<e1:AndTest> "and" <e2:NotTest> => ast::Expression::BoolOp { a: Box::new(e1), op: ast::BooleanOperator::And, b: Box::new(e2) },
|
||||
};
|
||||
|
||||
NotTest: ast::Expression = {
|
||||
"not" <e:NotTest> => ast::Expression::Unop { a: Box::new(e), op: ast::UnaryOperator::Not },
|
||||
<e:Comparison> => e,
|
||||
Comparison,
|
||||
};
|
||||
|
||||
Comparison: ast::Expression = {
|
||||
<e1:Comparison> <op:CompOp> <e2:Expression> => ast::Expression::Compare { a: Box::new(e1), op: op, b: Box::new(e2) },
|
||||
<e:Expression> => e,
|
||||
<e:Expression> <comparisons:(CompOp Expression)+> => {
|
||||
let mut vals = vec![e];
|
||||
let mut ops = vec![];
|
||||
for x in comparisons {
|
||||
ops.push(x.0);
|
||||
vals.push(x.1);
|
||||
}
|
||||
ast::Expression::Compare { vals, ops }
|
||||
},
|
||||
Expression,
|
||||
};
|
||||
|
||||
CompOp: ast::Comparison = {
|
||||
@@ -726,7 +684,7 @@ CompOp: ast::Comparison = {
|
||||
|
||||
Expression: ast::Expression = {
|
||||
<e1:Expression> "|" <e2:XorExpression> => ast::Expression::Binop { a: Box::new(e1), op: ast::Operator::BitOr, b: Box::new(e2) },
|
||||
<e:XorExpression> => e,
|
||||
XorExpression,
|
||||
};
|
||||
|
||||
XorExpression: ast::Expression = {
|
||||
@@ -776,7 +734,7 @@ Factor: ast::Expression = {
|
||||
"+" <e:Factor> => ast::Expression::Unop { a: Box::new(e), op: ast::UnaryOperator::Pos },
|
||||
"-" <e:Factor> => ast::Expression::Unop { a: Box::new(e), op: ast::UnaryOperator::Neg },
|
||||
"~" <e:Factor> => ast::Expression::Unop { a: Box::new(e), op: ast::UnaryOperator::Inv },
|
||||
<e:Power> => e,
|
||||
Power,
|
||||
};
|
||||
|
||||
Power: ast::Expression = {
|
||||
@@ -789,10 +747,20 @@ Power: ast::Expression = {
|
||||
};
|
||||
|
||||
AtomExpr: ast::Expression = {
|
||||
<e:Atom> => e,
|
||||
<f:AtomExpr> "(" <a:ArgumentList> ")" => ast::Expression::Call { function: Box::new(f), args: a.0, keywords: a.1 },
|
||||
<e:AtomExpr> "[" <s:SubscriptList> "]" => ast::Expression::Subscript { a: Box::new(e), b: Box::new(s) },
|
||||
<e:AtomExpr> "." <n:Identifier> => ast::Expression::Attribute { value: Box::new(e), name: n },
|
||||
<is_await:"await"?> <atom:AtomExpr2> => {
|
||||
if is_await.is_some() {
|
||||
ast::Expression::Await { value: Box::new(atom) }
|
||||
} else {
|
||||
atom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AtomExpr2: ast::Expression = {
|
||||
Atom,
|
||||
<f:AtomExpr2> "(" <a:ArgumentList> ")" => ast::Expression::Call { function: Box::new(f), args: a.0, keywords: a.1 },
|
||||
<e:AtomExpr2> "[" <s:SubscriptList> "]" => ast::Expression::Subscript { a: Box::new(e), b: Box::new(s) },
|
||||
<e:AtomExpr2> "." <n:Identifier> => ast::Expression::Attribute { value: Box::new(e), name: n },
|
||||
};
|
||||
|
||||
SubscriptList: ast::Expression = {
|
||||
@@ -810,7 +778,7 @@ SubscriptList: ast::Expression = {
|
||||
};
|
||||
|
||||
Subscript: ast::Expression = {
|
||||
<e:Test> => e,
|
||||
Test,
|
||||
<e1:Test?> ":" <e2:Test?> <e3:SliceOp?> => {
|
||||
let s1 = e1.unwrap_or(ast::Expression::None);
|
||||
let s2 = e2.unwrap_or(ast::Expression::None);
|
||||
@@ -824,30 +792,17 @@ SliceOp: ast::Expression = {
|
||||
}
|
||||
|
||||
Atom: ast::Expression = {
|
||||
<s:StringGroup> => ast::Expression::String { value: s },
|
||||
<b:Bytes> => ast::Expression::Bytes { value: b },
|
||||
<n:Number> => ast::Expression::Number { value: n },
|
||||
<i:Identifier> => ast::Expression::Identifier { name: i },
|
||||
<value:StringGroup> => ast::Expression::String { value },
|
||||
<value:Bytes> => ast::Expression::Bytes { value },
|
||||
<value:Number> => ast::Expression::Number { value },
|
||||
<name:Identifier> => ast::Expression::Identifier { name },
|
||||
"[" <e:TestListComp?> "]" => {
|
||||
let elements = e.unwrap_or(Vec::new());
|
||||
ast::Expression::List { elements }
|
||||
},
|
||||
"[" <e:TestListComp2> "]" => {
|
||||
// List comprehension:
|
||||
e
|
||||
},
|
||||
"(" <e:TestList?> <trailing_comma:","?> ")" => {
|
||||
match e {
|
||||
None => ast::Expression::Tuple { elements: Vec::new() },
|
||||
Some(elements) => {
|
||||
if elements.len() == 1 && trailing_comma.is_none() {
|
||||
// This is "(e)", which is equivalent to "e"
|
||||
elements.into_iter().next().unwrap()
|
||||
} else {
|
||||
ast::Expression::Tuple { elements }
|
||||
}
|
||||
}
|
||||
}
|
||||
"[" <e:TestListComp2> "]" => e,
|
||||
"(" <elements:TestList?> ")" => {
|
||||
elements.unwrap_or(ast::Expression::Tuple { elements: Vec::new() })
|
||||
},
|
||||
"(" <e:Test> <c:CompFor> ")" => {
|
||||
ast::Expression::Comprehension {
|
||||
@@ -866,9 +821,7 @@ Atom: ast::Expression = {
|
||||
};
|
||||
|
||||
TestListComp: Vec<ast::Expression> = {
|
||||
<e:OneOrMore<TestOrStarExpr>> <_trailing_comma:","?> => {
|
||||
e
|
||||
},
|
||||
<e:OneOrMore<TestOrStarExpr>> <_trailing_comma:","?> => e,
|
||||
};
|
||||
|
||||
TestListComp2: ast::Expression = {
|
||||
@@ -880,10 +833,8 @@ TestListComp2: ast::Expression = {
|
||||
},
|
||||
};
|
||||
|
||||
TestDict: Vec<(ast::Expression, ast::Expression)> = {
|
||||
<e1:OneOrMore<DictEntry>> <_trailing_comma:","?> => {
|
||||
e1
|
||||
}
|
||||
TestDict: Vec<(Option<ast::Expression>, ast::Expression)> = {
|
||||
<elements:OneOrMore<DictElement>> <_trailing_comma:","?> => elements,
|
||||
};
|
||||
|
||||
TestDictComp: ast::Expression = {
|
||||
@@ -899,10 +850,13 @@ DictEntry: (ast::Expression, ast::Expression) = {
|
||||
<e1: Test> ":" <e2: Test> => (e1, e2),
|
||||
};
|
||||
|
||||
DictElement: (Option<ast::Expression>, ast::Expression) = {
|
||||
<e:DictEntry> => (Some(e.0), e.1),
|
||||
"**" <e:Expression> => (None, e),
|
||||
};
|
||||
|
||||
TestSet: Vec<ast::Expression> = {
|
||||
<e1:OneOrMore<Test>> ","? => {
|
||||
e1
|
||||
}
|
||||
<e1:OneOrMore<Test>> ","? => e1
|
||||
};
|
||||
|
||||
TestSetComp: ast::Expression = {
|
||||
@@ -914,31 +868,37 @@ TestSetComp: ast::Expression = {
|
||||
}
|
||||
};
|
||||
|
||||
ExpressionOrStarExpression = {
|
||||
Expression,
|
||||
StarExpr
|
||||
};
|
||||
|
||||
ExpressionList: ast::Expression = {
|
||||
<e: ExpressionList2> => {
|
||||
if e.len() == 1 {
|
||||
e.into_iter().next().unwrap()
|
||||
<elements: OneOrMore<ExpressionOrStarExpression>> <trailing_comma:","?> => {
|
||||
if elements.len() == 1 && trailing_comma.is_none() {
|
||||
elements.into_iter().next().unwrap()
|
||||
} else {
|
||||
ast::Expression::Tuple { elements: e }
|
||||
ast::Expression::Tuple { elements }
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
ExpressionList2: Vec<ast::Expression> = {
|
||||
<e1:Expression> <e2:("," Expression)*> ","? => {
|
||||
let mut l = vec![e1];
|
||||
l.extend(e2.into_iter().map(|x| x.1));
|
||||
l
|
||||
},
|
||||
<elements:OneOrMore<Expression>> ","? => elements,
|
||||
};
|
||||
|
||||
#[inline]
|
||||
TestList: Vec<ast::Expression> = {
|
||||
<e1:Test> <e2: ("," Test)*> => {
|
||||
let mut l = vec![e1];
|
||||
l.extend(e2.into_iter().map(|x| x.1));
|
||||
l
|
||||
}
|
||||
// A test list is one of:
|
||||
// - a list of expressions
|
||||
// - a single expression
|
||||
// - a single expression followed by a trailing comma
|
||||
TestList: ast::Expression = {
|
||||
<elements:OneOrMore<Test>> <trailing_comma: ","?> => {
|
||||
if elements.len() == 1 && trailing_comma.is_none() {
|
||||
elements.into_iter().next().unwrap()
|
||||
} else {
|
||||
ast::Expression::Tuple { elements }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Test
|
||||
@@ -947,27 +907,16 @@ StarExpr: ast::Expression = {
|
||||
};
|
||||
|
||||
// Comprehensions:
|
||||
CompFor: Vec<ast::Comprehension> = {
|
||||
<c:SingleForComprehension+> => c,
|
||||
};
|
||||
CompFor: Vec<ast::Comprehension> = <c:SingleForComprehension+> => c;
|
||||
|
||||
SingleForComprehension: ast::Comprehension = {
|
||||
"for" <e:ExpressionList> "in" <i:OrTest> <c2:ComprehensionIf*> => {
|
||||
ast::Comprehension {
|
||||
target: e,
|
||||
iter: i,
|
||||
ifs: c2,
|
||||
}
|
||||
"for" <target:ExpressionList> "in" <iter:OrTest> <c2:ComprehensionIf*> => {
|
||||
ast::Comprehension { target, iter, ifs: c2 }
|
||||
}
|
||||
};
|
||||
|
||||
ExpressionNoCond: ast::Expression = {
|
||||
OrTest,
|
||||
};
|
||||
|
||||
ComprehensionIf: ast::Expression = {
|
||||
"if" <c:ExpressionNoCond> => c,
|
||||
};
|
||||
ExpressionNoCond: ast::Expression = OrTest;
|
||||
ComprehensionIf: ast::Expression = "if" <c:ExpressionNoCond> => c;
|
||||
|
||||
ArgumentList: (Vec<ast::Expression>, Vec<ast::Keyword>) = {
|
||||
<e: Comma<FunctionArgument>> => {
|
||||
@@ -979,8 +928,15 @@ ArgumentList: (Vec<ast::Expression>, Vec<ast::Keyword>) = {
|
||||
keywords.push(ast::Keyword { name: n, value: value });
|
||||
},
|
||||
None => {
|
||||
if keywords.len() > 0 {
|
||||
panic!("positional argument follows keyword argument");
|
||||
// Allow starred args after keyword arguments.
|
||||
let is_starred = if let ast::Expression::Starred { .. } = &value {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if keywords.len() > 0 && !is_starred {
|
||||
panic!("positional argument follows keyword argument {:?}", keywords);
|
||||
};
|
||||
args.push(value);
|
||||
},
|
||||
@@ -1024,8 +980,8 @@ OneOrMore<T>: Vec<T> = {
|
||||
};
|
||||
|
||||
Number: ast::Number = {
|
||||
<s:int> => { ast::Number::Integer { value: s } },
|
||||
<s:float> => { ast::Number::Float { value: s } },
|
||||
<value:int> => { ast::Number::Integer { value } },
|
||||
<value:float> => { ast::Number::Float { value } },
|
||||
<s:complex> => { ast::Number::Complex { real: s.0, imag: s.1 } },
|
||||
};
|
||||
|
||||
@@ -1115,6 +1071,8 @@ extern {
|
||||
"and" => lexer::Tok::And,
|
||||
"as" => lexer::Tok::As,
|
||||
"assert" => lexer::Tok::Assert,
|
||||
"async" => lexer::Tok::Async,
|
||||
"await" => lexer::Tok::Await,
|
||||
"break" => lexer::Tok::Break,
|
||||
"class" => lexer::Tok::Class,
|
||||
"continue" => lexer::Tok::Continue,
|
||||
|
||||
+3
-1
@@ -3,7 +3,7 @@
|
||||
use num_bigint::BigInt;
|
||||
|
||||
/// Python source code can be tokenized in a sequence of these tokens.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Tok {
|
||||
Name { name: String },
|
||||
Int { value: BigInt },
|
||||
@@ -72,6 +72,8 @@ pub enum Tok {
|
||||
And,
|
||||
As,
|
||||
Assert,
|
||||
Async,
|
||||
Await,
|
||||
Break,
|
||||
Class,
|
||||
Continue,
|
||||
|
||||
+85
-21
@@ -10,11 +10,18 @@ extern crate rustyline;
|
||||
use clap::{App, Arg};
|
||||
use rustpython_parser::error::ParseError;
|
||||
use rustpython_vm::{
|
||||
compile, error::CompileError, frame::Scope, import, obj::objstr, print_exception,
|
||||
pyobject::PyResult, util, VirtualMachine,
|
||||
compile,
|
||||
error::CompileError,
|
||||
error::CompileErrorType,
|
||||
frame::Scope,
|
||||
import,
|
||||
obj::objstr,
|
||||
print_exception,
|
||||
pyobject::{ItemProtocol, PyResult},
|
||||
util, VirtualMachine,
|
||||
};
|
||||
use rustyline::{error::ReadlineError, Editor};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
@@ -65,14 +72,12 @@ fn main() {
|
||||
}
|
||||
|
||||
fn _run_string(vm: &VirtualMachine, source: &str, source_path: String) -> PyResult {
|
||||
let code_obj =
|
||||
compile::compile(vm, source, &compile::Mode::Exec, source_path).map_err(|err| {
|
||||
let syntax_error = vm.context().exceptions.syntax_error.clone();
|
||||
vm.new_exception(syntax_error, err.to_string())
|
||||
})?;
|
||||
let code_obj = compile::compile(vm, source, &compile::Mode::Exec, source_path.clone())
|
||||
.map_err(|err| vm.new_syntax_error(&err))?;
|
||||
// trace!("Code object: {:?}", code_obj.borrow());
|
||||
let vars = vm.ctx.new_scope(); // Keep track of local variables
|
||||
vm.run_code_obj(code_obj, vars)
|
||||
let attrs = vm.ctx.new_dict();
|
||||
attrs.set_item("__file__", vm.new_str(source_path), vm)?;
|
||||
vm.run_code_obj(code_obj, Scope::with_builtins(None, attrs, vm))
|
||||
}
|
||||
|
||||
fn handle_exception(vm: &VirtualMachine, result: PyResult) {
|
||||
@@ -99,29 +104,85 @@ fn run_module(vm: &VirtualMachine, module: &str) -> PyResult {
|
||||
fn run_script(vm: &VirtualMachine, script_file: &str) -> PyResult {
|
||||
debug!("Running file {}", script_file);
|
||||
// Parse an ast from it:
|
||||
let file_path = Path::new(script_file);
|
||||
match util::read_file(file_path) {
|
||||
let file_path = PathBuf::from(script_file);
|
||||
let file_path = if file_path.is_file() {
|
||||
file_path
|
||||
} else if file_path.is_dir() {
|
||||
let main_file_path = file_path.join("__main__.py");
|
||||
if main_file_path.is_file() {
|
||||
main_file_path
|
||||
} else {
|
||||
error!(
|
||||
"can't find '__main__' module in '{}'",
|
||||
file_path.to_str().unwrap()
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
"can't open file '{}': No such file or directory",
|
||||
file_path.to_str().unwrap()
|
||||
);
|
||||
std::process::exit(1);
|
||||
};
|
||||
|
||||
match util::read_file(&file_path) {
|
||||
Ok(source) => _run_string(vm, &source, file_path.to_str().unwrap().to_string()),
|
||||
Err(err) => {
|
||||
error!("Failed reading file: {:?}", err.kind());
|
||||
error!(
|
||||
"Failed reading file '{}': {:?}",
|
||||
file_path.to_str().unwrap(),
|
||||
err.kind()
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run_script() {
|
||||
let vm = VirtualMachine::new();
|
||||
|
||||
// test file run
|
||||
let r = run_script(&vm, "tests/snippets/dir_main/__main__.py");
|
||||
assert!(r.is_ok());
|
||||
|
||||
// test module run
|
||||
let r = run_script(&vm, "tests/snippets/dir_main");
|
||||
assert!(r.is_ok());
|
||||
}
|
||||
|
||||
fn shell_exec(vm: &VirtualMachine, source: &str, scope: Scope) -> Result<(), CompileError> {
|
||||
match compile::compile(vm, source, &compile::Mode::Single, "<stdin>".to_string()) {
|
||||
Ok(code) => {
|
||||
if let Err(err) = vm.run_code_obj(code, scope) {
|
||||
print_exception(vm, &err);
|
||||
match vm.run_code_obj(code, scope.clone()) {
|
||||
Ok(value) => {
|
||||
// Save non-None values as "_"
|
||||
|
||||
use rustpython_vm::pyobject::{IdProtocol, IntoPyObject};
|
||||
|
||||
if !value.is(&vm.get_none()) {
|
||||
let key = objstr::PyString::from("_").into_pyobject(vm);
|
||||
scope.globals.set_item(key, value, vm).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Err(err) => {
|
||||
print_exception(vm, &err);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Don't inject syntax errors for line continuation
|
||||
Err(err @ CompileError::Parse(ParseError::EOF(_))) => Err(err),
|
||||
Err(
|
||||
err @ CompileError {
|
||||
error: CompileErrorType::Parse(ParseError::EOF(_)),
|
||||
..
|
||||
},
|
||||
) => Err(err),
|
||||
Err(err) => {
|
||||
let syntax_error = vm.context().exceptions.syntax_error.clone();
|
||||
let exc = vm.new_exception(syntax_error, format!("{}", err));
|
||||
let exc = vm.new_syntax_error(&err);
|
||||
print_exception(vm, &exc);
|
||||
Err(err)
|
||||
}
|
||||
@@ -153,10 +214,10 @@ fn get_prompt(vm: &VirtualMachine, prompt_name: &str) -> String {
|
||||
|
||||
fn run_shell(vm: &VirtualMachine) -> PyResult {
|
||||
println!(
|
||||
"Welcome to the magnificent Rust Python {} interpreter",
|
||||
"Welcome to the magnificent Rust Python {} interpreter \u{1f631} \u{1f596}",
|
||||
crate_version!()
|
||||
);
|
||||
let vars = vm.ctx.new_scope();
|
||||
let vars = vm.new_scope_with_builtins();
|
||||
|
||||
// Read a single line:
|
||||
let mut input = String::new();
|
||||
@@ -192,7 +253,10 @@ fn run_shell(vm: &VirtualMachine) -> PyResult {
|
||||
}
|
||||
|
||||
match shell_exec(vm, &input, vars.clone()) {
|
||||
Err(CompileError::Parse(ParseError::EOF(_))) => {
|
||||
Err(CompileError {
|
||||
error: CompileErrorType::Parse(ParseError::EOF(_)),
|
||||
..
|
||||
}) => {
|
||||
continuing = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
snippets/whats_left_to_implement.py
|
||||
@@ -13,6 +13,7 @@ pip install pipenv
|
||||
if [ $CODE_COVERAGE = "true" ]
|
||||
then
|
||||
find . -name '*.gcda' -delete
|
||||
find . -name '*.gcno' -delete
|
||||
|
||||
export CARGO_INCREMENTAL=0
|
||||
export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Zno-landing-pads"
|
||||
@@ -31,7 +32,7 @@ then
|
||||
zip -0 ccov.zip `find . \( -name "rustpython*.gc*" \) -print`
|
||||
|
||||
# Install grcov
|
||||
curl -L https://github.com/mozilla/grcov/releases/download/v0.4.1/grcov-linux-x86_64.tar.bz2 | tar jxf -
|
||||
curl -L https://github.com/mozilla/grcov/releases/download/v0.4.2/grcov-linux-x86_64.tar.bz2 | tar jxf -
|
||||
|
||||
./grcov ccov.zip -s . -t lcov --llvm --branch --ignore-not-existing --ignore-dir "/*" -p "x" > lcov.info
|
||||
|
||||
|
||||
+4
-2
@@ -1,4 +1,3 @@
|
||||
|
||||
# Test snippets
|
||||
|
||||
This directory contains two sets of test snippets which can be run in
|
||||
@@ -6,6 +5,10 @@ Python. The `snippets/` directory contains functional tests, and the
|
||||
`benchmarks/` directory contains snippets for use in benchmarking
|
||||
RustPython's performance.
|
||||
|
||||
## Generates the test for not implemented methods
|
||||
|
||||
run using cpython not_impl_gen.py it automatically generate a
|
||||
test snippet to check not yet implemented methods
|
||||
|
||||
## Running with CPython + RustPython
|
||||
|
||||
@@ -15,4 +18,3 @@ compilation to bytecode. When this is done, run the bytecode with rustpython.
|
||||
## Running with RustPython
|
||||
|
||||
The other option is to run all snippets with RustPython.
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
not_implemented = [(name, method)
|
||||
for name, (val, methods) in expected_methods.items()
|
||||
for method in methods
|
||||
if not hasattr(val, method)]
|
||||
|
||||
for r in not_implemented:
|
||||
print(r[0], ".", r[1], sep="")
|
||||
if not not_implemented:
|
||||
print("Not much \\o/")
|
||||
|
||||
if platform.python_implementation() == "CPython":
|
||||
assert len(not_implemented) == 0, "CPython should have all the methods"
|
||||
@@ -0,0 +1,7 @@
|
||||
# WARNING: THIS IS AN AUTOMATICALLY GENERATED FILE
|
||||
# EDIT tests/not_impl_gen.py, NOT THIS FILE.
|
||||
# RESULTS OF THIS TEST DEPEND ON THE CPYTHON
|
||||
# VERSION USED TO RUN not_impl_gen.py
|
||||
|
||||
import platform
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
objects = [
|
||||
bool,
|
||||
bytearray,
|
||||
bytes,
|
||||
complex,
|
||||
dict,
|
||||
float,
|
||||
frozenset,
|
||||
int,
|
||||
list,
|
||||
memoryview,
|
||||
range,
|
||||
set,
|
||||
str,
|
||||
tuple,
|
||||
object,
|
||||
]
|
||||
|
||||
|
||||
def attr_is_not_inherited(type_, attr):
|
||||
"""
|
||||
returns True if type_'s attr is not inherited from any of its base classes
|
||||
"""
|
||||
|
||||
bases = obj.__mro__[1:]
|
||||
|
||||
return getattr(obj, attr) not in (
|
||||
getattr(base, attr, None) for base in bases)
|
||||
|
||||
|
||||
header = open("generator/not_impl_header.txt")
|
||||
footer = open("generator/not_impl_footer.txt")
|
||||
output = open("snippets/whats_left_to_implement.py", "w")
|
||||
|
||||
output.write(header.read())
|
||||
output.write("expected_methods = {\n")
|
||||
|
||||
for obj in objects:
|
||||
output.write(f" '{obj.__name__}': ({obj.__name__}, [\n")
|
||||
output.write("\n".join(
|
||||
f" {attr!r},"
|
||||
for attr in dir(obj)
|
||||
if attr_is_not_inherited(obj, attr)
|
||||
))
|
||||
output.write("\n ])," + ("\n" if objects[-1] == obj else "\n\n"))
|
||||
|
||||
output.write("}\n\n")
|
||||
output.write(footer.read())
|
||||
@@ -1,2 +0,0 @@
|
||||
x = []
|
||||
x.append(1)
|
||||
@@ -21,3 +21,10 @@ print(foo.body[0].value.func.id)
|
||||
assert foo.body[0].value.func.id == 'print'
|
||||
assert foo.body[0].lineno == 3
|
||||
assert foo.body[1].lineno == 4
|
||||
|
||||
n = ast.parse("3 < 4 > 5\n")
|
||||
assert n.body[0].value.left.n == 3
|
||||
assert 'Lt' in str(n.body[0].value.ops[0])
|
||||
assert 'Gt' in str(n.body[0].value.ops[1])
|
||||
assert n.body[0].value.comparators[0].n == 4
|
||||
assert n.body[0].value.comparators[1].n == 5
|
||||
@@ -51,3 +51,29 @@ assert True > 0
|
||||
assert int(True) == 1
|
||||
assert True.conjugate() == 1
|
||||
assert isinstance(True.conjugate(), int)
|
||||
|
||||
# Boolean operations on pairs of Bools should return Bools, not ints
|
||||
assert (False | True) is True
|
||||
assert (False & True) is False
|
||||
assert (False ^ True) is True
|
||||
# But only if both are Bools
|
||||
assert (False | 1) is not True
|
||||
assert (0 | True) is not True
|
||||
assert (False & 1) is not False
|
||||
assert (0 & True) is not False
|
||||
assert (False ^ 1) is not True
|
||||
assert (0 ^ True) is not True
|
||||
|
||||
# Check that the same works with __XXX__ methods
|
||||
assert False.__or__(0) is not False
|
||||
assert False.__or__(False) is False
|
||||
assert False.__ror__(0) is not False
|
||||
assert False.__ror__(False) is False
|
||||
assert False.__and__(0) is not False
|
||||
assert False.__and__(False) is False
|
||||
assert False.__rand__(0) is not False
|
||||
assert False.__rand__(False) is False
|
||||
assert False.__xor__(0) is not False
|
||||
assert False.__xor__(False) is False
|
||||
assert False.__rxor__(0) is not False
|
||||
assert False.__rxor__(False) is False
|
||||
@@ -0,0 +1,10 @@
|
||||
from testutils import assert_raises
|
||||
from testutils import TestFailingBool, TestFailingIter
|
||||
|
||||
assert all([True])
|
||||
assert not all([False])
|
||||
assert all([])
|
||||
assert not all([False, TestFailingBool()])
|
||||
|
||||
assert_raises(RuntimeError, lambda: all(TestFailingIter()))
|
||||
assert_raises(RuntimeError, lambda: all([TestFailingBool()]))
|
||||
@@ -1,13 +1,10 @@
|
||||
assert any([1]);
|
||||
assert not any([]);
|
||||
assert not any([0,0,0,0]);
|
||||
assert any([0,0,1,0,0]);
|
||||
def anything(a):
|
||||
return a
|
||||
from testutils import assert_raises
|
||||
from testutils import TestFailingBool, TestFailingIter
|
||||
|
||||
class Test:
|
||||
def __iter__(self):
|
||||
while True:
|
||||
yield True
|
||||
assert any([True])
|
||||
assert not any([False])
|
||||
assert not any([])
|
||||
assert any([True, TestFailingBool()])
|
||||
|
||||
assert any(map(anything, Test()))
|
||||
assert_raises(RuntimeError, lambda: any(TestFailingIter()))
|
||||
assert_raises(RuntimeError, lambda: any([TestFailingBool()]))
|
||||
@@ -1,9 +1,8 @@
|
||||
assert not callable(1)
|
||||
def f(): pass
|
||||
# TODO uncomment when callable types get unified __call__ (or equivalent)
|
||||
#assert callable(f)
|
||||
#assert callable(len)
|
||||
#assert callable(lambda: 1)
|
||||
assert callable(f)
|
||||
assert callable(len)
|
||||
assert callable(lambda: 1)
|
||||
assert callable(int)
|
||||
|
||||
class C:
|
||||
@@ -13,7 +12,7 @@ class C:
|
||||
def f(self): pass
|
||||
assert callable(C)
|
||||
assert not callable(C())
|
||||
#assert callable(C().f)
|
||||
assert callable(C().f)
|
||||
|
||||
class C:
|
||||
def __call__(self): pass
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
from testutils import assert_raises
|
||||
|
||||
assert "a" == chr(97)
|
||||
assert "é" == chr(233)
|
||||
assert "🤡" == chr(129313)
|
||||
|
||||
assert_raises(TypeError, lambda: chr(), "chr() takes exactly one argument (0 given)")
|
||||
assert_raises(ValueError, lambda: chr(0x110005), "ValueError: chr() arg not in range(0x110000)")
|
||||
@@ -1,3 +1,5 @@
|
||||
from testutils import assert_raises
|
||||
|
||||
# __abs__
|
||||
|
||||
assert abs(complex(3, 4)) == 5
|
||||
@@ -21,26 +23,115 @@ assert bool(complex(1, 0))
|
||||
assert complex(1, 2) != complex(1, 1)
|
||||
assert complex(1, 2) != 'foo'
|
||||
assert complex(1, 2).__eq__('foo') == NotImplemented
|
||||
assert 1j != 10 ** 1000
|
||||
|
||||
# __mul__, __rmul__
|
||||
|
||||
assert complex(2, -3) * complex(-5, 7) == complex(11, 29)
|
||||
assert complex(2, -3) * 5 == complex(10, -15)
|
||||
assert 5 * complex(2, -3) == complex(2, -3) * 5
|
||||
|
||||
# __truediv__, __rtruediv__
|
||||
|
||||
assert complex(2, -3) / 2 == complex(1, -1.5)
|
||||
assert 5 / complex(3, -4) == complex(0.6, 0.8)
|
||||
|
||||
# __mod__, __rmod__
|
||||
|
||||
assert_raises(
|
||||
TypeError,
|
||||
lambda: complex(2, -3) % 2,
|
||||
"can't mod complex numbers.")
|
||||
assert_raises(
|
||||
TypeError,
|
||||
lambda: 2 % complex(2, -3),
|
||||
"can't mod complex numbers.")
|
||||
|
||||
# __floordiv__, __rfloordiv__
|
||||
|
||||
assert_raises(
|
||||
TypeError,
|
||||
lambda: complex(2, -3) // 2,
|
||||
"can't take floor of complex number.")
|
||||
assert_raises(
|
||||
TypeError,
|
||||
lambda: 2 // complex(2, -3),
|
||||
"can't take floor of complex number.")
|
||||
|
||||
# __divmod__, __rdivmod__
|
||||
|
||||
assert_raises(
|
||||
TypeError,
|
||||
lambda: divmod(complex(2, -3), 2),
|
||||
"can't take floor or mod of complex number.")
|
||||
assert_raises(
|
||||
TypeError,
|
||||
lambda: divmod(2, complex(2, -3)),
|
||||
"can't take floor or mod of complex number.")
|
||||
|
||||
# __pow__, __rpow__
|
||||
|
||||
# assert 1j ** 2 == -1
|
||||
assert complex(1) ** 2 == 1
|
||||
assert 2 ** complex(2) == 4
|
||||
|
||||
# __neg__
|
||||
|
||||
assert -complex(1, -1) == complex(-1, 1)
|
||||
assert -complex(0, 0) == complex(0, 0)
|
||||
|
||||
# real
|
||||
# __bool__
|
||||
|
||||
assert bool(complex(0, 0)) is False
|
||||
assert bool(complex(0, 1)) is True
|
||||
assert bool(complex(1, 0)) is True
|
||||
|
||||
# __hash__
|
||||
|
||||
assert hash(complex(1)) == hash(float(1)) == hash(int(1))
|
||||
assert hash(complex(-1)) == hash(float(-1)) == hash(int(-1))
|
||||
assert hash(complex(3.14)) == hash(float(3.14))
|
||||
assert hash(complex(-float('inf'))) == hash(-float('inf'))
|
||||
assert hash(1j) != hash(1)
|
||||
|
||||
# numbers.Complex
|
||||
|
||||
a = complex(3, 4)
|
||||
b = 4j
|
||||
assert a.real == 3
|
||||
assert b.real == 0
|
||||
|
||||
# imag
|
||||
|
||||
assert a.imag == 4
|
||||
assert b.imag == 4
|
||||
|
||||
assert a.conjugate() == 3 - 4j
|
||||
assert b.conjugate() == -4j
|
||||
|
||||
# int and complex addition
|
||||
assert 1 + 1j == complex(1, 1)
|
||||
assert 1j + 1 == complex(1, 1)
|
||||
assert (1j + 1) + 3 == complex(4, 1)
|
||||
assert 3 + (1j + 1) == complex(4, 1)
|
||||
|
||||
# float and complex addition
|
||||
assert 1.1 + 1.2j == complex(1.1, 1.2)
|
||||
assert 1.3j + 1.4 == complex(1.4, 1.3)
|
||||
assert (1.5j + 1.6) + 3 == complex(4.6, 1.5)
|
||||
assert 3.5 + (1.1j + 1.2) == complex(4.7, 1.1)
|
||||
|
||||
# subtraction
|
||||
assert 1 - 1j == complex(1, -1)
|
||||
assert 1j - 1 == complex(-1, 1)
|
||||
assert 2j - 1j == complex(0, 1)
|
||||
|
||||
# type error addition
|
||||
assert_raises(TypeError, lambda: 1j + 'str')
|
||||
assert_raises(TypeError, lambda: 1j - 'str')
|
||||
assert_raises(TypeError, lambda: 'str' + 1j)
|
||||
assert_raises(TypeError, lambda: 'str' - 1j)
|
||||
|
||||
# overflow
|
||||
msg = 'int too large to convert to float'
|
||||
assert_raises(OverflowError, lambda: complex(10 ** 1000, 0), msg)
|
||||
assert_raises(OverflowError, lambda: complex(0, 10 ** 1000), msg)
|
||||
assert_raises(OverflowError, lambda: 0j + 10 ** 1000, msg)
|
||||
@@ -1,3 +1,5 @@
|
||||
assert isinstance(dir(), list)
|
||||
assert '__builtins__' in dir()
|
||||
|
||||
class A:
|
||||
def test():
|
||||
@@ -8,6 +10,9 @@ a = A()
|
||||
assert "test" in dir(a), "test not in a"
|
||||
assert "test" in dir(A), "test not in A"
|
||||
|
||||
a.x = 3
|
||||
assert "x" in dir(a), "x not in a"
|
||||
|
||||
class B(A):
|
||||
def __dir__(self):
|
||||
return ('q', 'h')
|
||||
@@ -18,6 +23,16 @@ assert ['h', 'q'] == dir(B())
|
||||
# This calls type.__dir__ so isn't changed (but inheritance works)!
|
||||
assert 'test' in dir(A)
|
||||
|
||||
# eval() takes any mapping-like type, so dir() must support them
|
||||
# TODO: eval() should take any mapping as locals, not just dict-derived types
|
||||
class A(dict):
|
||||
def __getitem__(self, x):
|
||||
return dir
|
||||
def keys(self):
|
||||
yield 6
|
||||
yield 5
|
||||
assert eval("dir()", {}, A()) == [5, 6]
|
||||
|
||||
import socket
|
||||
|
||||
assert "AF_INET" in dir(socket)
|
||||
@@ -0,0 +1,7 @@
|
||||
import os
|
||||
|
||||
from import_file import import_file
|
||||
|
||||
import_file()
|
||||
|
||||
assert os.path.basename(__file__) == "builtin_file.py"
|
||||
@@ -7,3 +7,8 @@ assert_raises(TypeError, lambda: format(2, 3), 'format called with number')
|
||||
assert format({}) == "{}"
|
||||
|
||||
assert_raises(TypeError, lambda: format({}, 'b'), 'format_spec not empty for dict')
|
||||
|
||||
class BadFormat:
|
||||
def __format__(self, spec):
|
||||
return 42
|
||||
assert_raises(TypeError, lambda: format(BadFormat()))
|
||||
@@ -6,5 +6,14 @@ assert 'RustPython' in fd.read()
|
||||
assert_raises(FileNotFoundError, lambda: open('DoesNotExist'))
|
||||
|
||||
# Use open as a context manager
|
||||
with open('README.md') as fp:
|
||||
fp.read()
|
||||
with open('README.md', 'rt') as fp:
|
||||
contents = fp.read()
|
||||
assert type(contents) == str, "type is " + str(type(contents))
|
||||
|
||||
with open('README.md', 'r') as fp:
|
||||
contents = fp.read()
|
||||
assert type(contents) == str, "type is " + str(type(contents))
|
||||
|
||||
with open('README.md', 'rb') as fp:
|
||||
contents = fp.read()
|
||||
assert type(contents) == bytes, "type is " + str(type(contents))
|
||||
@@ -17,6 +17,7 @@ assert_raises(ValueError, lambda: range(10).index(-1), 'out of bounds')
|
||||
assert_raises(ValueError, lambda: range(10).index(10), 'out of bounds')
|
||||
assert_raises(ValueError, lambda: range(4, 10, 2).index(5), 'out of step')
|
||||
assert_raises(ValueError, lambda: range(10).index('foo'), 'not an int')
|
||||
assert_raises(ValueError, lambda: range(1, 10, 0), 'step is zero')
|
||||
|
||||
# count tests
|
||||
assert range(10).count(2) == 1
|
||||
@@ -27,6 +28,11 @@ assert range(4, 10, 2).count(4) == 1
|
||||
assert range(4, 10, 2).count(7) == 0
|
||||
assert range(10).count("foo") == 0
|
||||
|
||||
# __eq__
|
||||
assert range(1, 2, 3) == range(1, 2, 3)
|
||||
assert range(1, 2, 1) == range(1, 2)
|
||||
assert range(2) == range(0, 2)
|
||||
|
||||
# __bool__
|
||||
assert bool(range(1))
|
||||
assert bool(range(1, 2))
|
||||
@@ -50,3 +56,13 @@ assert 'foo' not in range(10)
|
||||
assert list(reversed(range(5))) == [4, 3, 2, 1, 0]
|
||||
assert list(reversed(range(5, 0, -1))) == [1, 2, 3, 4, 5]
|
||||
assert list(reversed(range(1,10,5))) == [6, 1]
|
||||
|
||||
# range retains the original int refs
|
||||
i = 2**64
|
||||
assert range(i).stop is i
|
||||
|
||||
# negative index
|
||||
assert range(10)[-1] == 9
|
||||
assert_raises(IndexError, lambda: range(10)[-11], 'out of bound')
|
||||
assert range(10)[-2:4] == range(8, 4)
|
||||
assert range(10)[-6:-2] == range(4, 8)
|
||||
@@ -2,24 +2,25 @@ from testutils import assert_raises
|
||||
|
||||
a = []
|
||||
assert a[:] == []
|
||||
assert a[:2**100] == []
|
||||
assert a[-2**100:] == []
|
||||
assert a[::2**100] == []
|
||||
assert a[: 2 ** 100] == []
|
||||
assert a[-2 ** 100 :] == []
|
||||
assert a[:: 2 ** 100] == []
|
||||
assert a[10:20] == []
|
||||
assert a[-20:-10] == []
|
||||
|
||||
b = [1, 2]
|
||||
|
||||
assert b[:] == [1, 2]
|
||||
assert b[:2**100] == [1, 2]
|
||||
assert b[-2**100:] == [1, 2]
|
||||
assert b[2**100:] == []
|
||||
assert b[::2**100] == [1]
|
||||
assert b[slice(None)] == [1, 2]
|
||||
assert b[: 2 ** 100] == [1, 2]
|
||||
assert b[-2 ** 100 :] == [1, 2]
|
||||
assert b[2 ** 100 :] == []
|
||||
assert b[:: 2 ** 100] == [1]
|
||||
assert b[-10:1] == [1]
|
||||
assert b[0:0] == []
|
||||
assert b[1:0] == []
|
||||
|
||||
assert_raises(ValueError, lambda: b[::0], 'zero step slice')
|
||||
assert_raises(ValueError, lambda: b[::0], "zero step slice")
|
||||
|
||||
assert b[::-1] == [2, 1]
|
||||
assert b[1::-1] == [2, 1]
|
||||
@@ -33,7 +34,7 @@ c = list(range(10))
|
||||
assert c[9:6:-3] == [9]
|
||||
assert c[9::-3] == [9, 6, 3, 0]
|
||||
assert c[9::-4] == [9, 5, 1]
|
||||
assert c[8::-2**100] == [8]
|
||||
assert c[8 :: -2 ** 100] == [8]
|
||||
|
||||
assert c[7:7:-2] == []
|
||||
assert c[7:8:-2] == []
|
||||
@@ -43,6 +44,7 @@ d = "123456"
|
||||
assert d[3::-1] == "4321"
|
||||
assert d[4::-3] == "52"
|
||||
|
||||
assert [1, 2, 3, 5, 6][-1:-5:-1] == [6, 5, 3, 2] # #746
|
||||
|
||||
slice_a = slice(5)
|
||||
assert slice_a.start is None
|
||||
@@ -59,6 +61,12 @@ assert slice_c.start == 1
|
||||
assert slice_c.stop == 5
|
||||
assert slice_c.step == 2
|
||||
|
||||
a = object()
|
||||
slice_d = slice(a, "v", 1.0)
|
||||
assert slice_d.start is a
|
||||
assert slice_d.stop == "v"
|
||||
assert slice_d.step == 1.0
|
||||
|
||||
|
||||
class SubScript(object):
|
||||
def __getitem__(self, item):
|
||||
@@ -71,3 +79,53 @@ class SubScript(object):
|
||||
ss = SubScript()
|
||||
_ = ss[:]
|
||||
ss[:1] = 1
|
||||
|
||||
|
||||
class CustomIndex:
|
||||
def __init__(self, x):
|
||||
self.x = x
|
||||
|
||||
def __index__(self):
|
||||
return self.x
|
||||
|
||||
|
||||
assert c[CustomIndex(1):CustomIndex(3)] == [1, 2]
|
||||
assert d[CustomIndex(1):CustomIndex(3)] == "23"
|
||||
|
||||
|
||||
def test_all_slices():
|
||||
"""
|
||||
test all possible slices except big number
|
||||
"""
|
||||
|
||||
mod = __import__('cpython_generated_slices')
|
||||
|
||||
ll = mod.LL
|
||||
start = mod.START
|
||||
end = mod.END
|
||||
step = mod.STEP
|
||||
slices_res = mod.SLICES_RES
|
||||
|
||||
count = 0
|
||||
failures = []
|
||||
for s in start:
|
||||
for e in end:
|
||||
for t in step:
|
||||
lhs = ll[s:e:t]
|
||||
try:
|
||||
assert lhs == slices_res[count]
|
||||
except AssertionError:
|
||||
failures.append(
|
||||
"start: {} ,stop: {}, step {}. Expected: {}, found: {}".format(
|
||||
s, e, t, lhs, slices_res[count]
|
||||
)
|
||||
)
|
||||
count += 1
|
||||
|
||||
if failures:
|
||||
for f in failures:
|
||||
print(f)
|
||||
print(len(failures), "slices failed")
|
||||
|
||||
|
||||
test_all_slices()
|
||||
@@ -0,0 +1,28 @@
|
||||
from testutils import assertRaises
|
||||
|
||||
assert '__builtins__' in globals()
|
||||
# assert type(__builtins__).__name__ == 'module'
|
||||
with assertRaises(AttributeError):
|
||||
__builtins__.__builtins__
|
||||
|
||||
__builtins__.x = 'new'
|
||||
assert x == 'new'
|
||||
|
||||
exec('assert "__builtins__" in globals()', dict())
|
||||
exec('assert __builtins__ == 7', {'__builtins__': 7})
|
||||
exec('assert not isinstance(__builtins__, dict)')
|
||||
exec('assert isinstance(__builtins__, dict)', {})
|
||||
|
||||
namespace = {}
|
||||
exec('', namespace)
|
||||
assert namespace['__builtins__'] == __builtins__.__dict__
|
||||
|
||||
# with assertRaises(NameError):
|
||||
# exec('print(__builtins__)', {'__builtins__': {}})
|
||||
|
||||
# __builtins__ is deletable but names are alive
|
||||
del __builtins__
|
||||
with assertRaises(NameError):
|
||||
__builtins__
|
||||
|
||||
assert print
|
||||
+673
-32
@@ -1,53 +1,605 @@
|
||||
#__getitem__ not implemented yet
|
||||
#a = bytearray(b'abc')
|
||||
#assert a[0] == b'a'
|
||||
#assert a[1] == b'b'
|
||||
from testutils import assertRaises
|
||||
|
||||
assert len(bytearray([1,2,3])) == 3
|
||||
# new
|
||||
assert bytearray([1, 2, 3])
|
||||
assert bytearray((1, 2, 3))
|
||||
assert bytearray(range(4))
|
||||
assert bytearray(3)
|
||||
assert b"bla"
|
||||
assert (
|
||||
bytearray("bla", "utf8") == bytearray("bla", encoding="utf-8") == bytearray(b"bla")
|
||||
)
|
||||
with assertRaises(TypeError):
|
||||
bytearray("bla")
|
||||
with assertRaises(TypeError):
|
||||
bytearray("bla", encoding=b"jilj")
|
||||
|
||||
assert bytearray(b'1a23').isalnum()
|
||||
assert not bytearray(b'1%a23').isalnum()
|
||||
assert bytearray(
|
||||
b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
|
||||
) == bytearray(range(0, 256))
|
||||
assert bytearray(
|
||||
b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
|
||||
) == bytearray(range(0, 256))
|
||||
assert bytearray(b"omkmok\Xaa") == bytearray(
|
||||
[111, 109, 107, 109, 111, 107, 92, 88, 97, 97]
|
||||
)
|
||||
|
||||
assert bytearray(b'abc').isalpha()
|
||||
assert not bytearray(b'abc1').isalpha()
|
||||
|
||||
a = bytearray(b"abcd")
|
||||
b = bytearray(b"ab")
|
||||
c = bytearray(b"abcd")
|
||||
|
||||
|
||||
# repr
|
||||
assert repr(bytearray([0, 1, 2])) == repr(bytearray(b"\x00\x01\x02"))
|
||||
assert (
|
||||
repr(bytearray([0, 1, 9, 10, 11, 13, 31, 32, 33, 89, 120, 255]))
|
||||
== "bytearray(b'\\x00\\x01\\t\\n\\x0b\\r\\x1f !Yx\\xff')"
|
||||
)
|
||||
assert repr(bytearray(b"abcd")) == "bytearray(b'abcd')"
|
||||
|
||||
# len
|
||||
assert len(bytearray("abcdé", "utf8")) == 6
|
||||
|
||||
# comp
|
||||
assert a == b"abcd"
|
||||
assert a > b
|
||||
assert a >= b
|
||||
assert b < a
|
||||
assert b <= a
|
||||
|
||||
assert bytearray(b"foobar").__eq__(2) == NotImplemented
|
||||
assert bytearray(b"foobar").__ne__(2) == NotImplemented
|
||||
assert bytearray(b"foobar").__gt__(2) == NotImplemented
|
||||
assert bytearray(b"foobar").__ge__(2) == NotImplemented
|
||||
assert bytearray(b"foobar").__lt__(2) == NotImplemented
|
||||
assert bytearray(b"foobar").__le__(2) == NotImplemented
|
||||
|
||||
# # hash
|
||||
with assertRaises(TypeError):
|
||||
hash(bytearray(b"abcd")) # unashable
|
||||
|
||||
# # iter
|
||||
[i for i in bytearray(b"abcd")] == ["a", "b", "c", "d"]
|
||||
assert list(bytearray(3)) == [0, 0, 0]
|
||||
|
||||
# add
|
||||
assert a + b == bytearray(b"abcdab")
|
||||
|
||||
# contains
|
||||
assert bytearray(b"ab") in bytearray(b"abcd")
|
||||
assert bytearray(b"cd") in bytearray(b"abcd")
|
||||
assert bytearray(b"abcd") in bytearray(b"abcd")
|
||||
assert bytearray(b"a") in bytearray(b"abcd")
|
||||
assert bytearray(b"d") in bytearray(b"abcd")
|
||||
assert bytearray(b"dc") not in bytearray(b"abcd")
|
||||
assert 97 in bytearray(b"abcd")
|
||||
assert 150 not in bytearray(b"abcd")
|
||||
with assertRaises(ValueError):
|
||||
350 in bytearray(b"abcd")
|
||||
|
||||
|
||||
# getitem
|
||||
d = bytearray(b"abcdefghij")
|
||||
|
||||
assert d[1] == 98
|
||||
assert d[-1] == 106
|
||||
assert d[2:6] == bytearray(b"cdef")
|
||||
assert d[-6:] == bytearray(b"efghij")
|
||||
assert d[1:8:2] == bytearray(b"bdfh")
|
||||
assert d[8:1:-2] == bytearray(b"igec")
|
||||
|
||||
|
||||
# # is_xx methods
|
||||
|
||||
assert bytearray(b"1a23").isalnum()
|
||||
assert not bytearray(b"1%a23").isalnum()
|
||||
|
||||
assert bytearray(b"abc").isalpha()
|
||||
assert not bytearray(b"abc1").isalpha()
|
||||
|
||||
# travis doesn't like this
|
||||
#assert bytearray(b'xyz').isascii()
|
||||
#assert not bytearray([128, 157, 32]).isascii()
|
||||
# assert bytearray(b'xyz').isascii()
|
||||
# assert not bytearray([128, 157, 32]).isascii()
|
||||
|
||||
assert bytearray(b'1234567890').isdigit()
|
||||
assert not bytearray(b'12ab').isdigit()
|
||||
assert bytearray(b"1234567890").isdigit()
|
||||
assert not bytearray(b"12ab").isdigit()
|
||||
|
||||
l = bytearray(b"lower")
|
||||
b = bytearray(b"UPPER")
|
||||
|
||||
l = bytearray(b'lower')
|
||||
assert l.islower()
|
||||
assert not l.isupper()
|
||||
assert l.upper().isupper()
|
||||
assert not bytearray(b'Super Friends').islower()
|
||||
assert b.isupper()
|
||||
assert not bytearray(b"Super Friends").islower()
|
||||
|
||||
assert bytearray(b' \n\t').isspace()
|
||||
assert not bytearray(b'\td\n').isspace()
|
||||
assert bytearray(b" \n\t").isspace()
|
||||
assert not bytearray(b"\td\n").isspace()
|
||||
|
||||
b = bytearray(b'UPPER')
|
||||
assert b.isupper()
|
||||
assert not b.islower()
|
||||
assert b.lower().islower()
|
||||
assert not bytearray(b'tuPpEr').isupper()
|
||||
assert l.islower()
|
||||
assert not bytearray(b"tuPpEr").isupper()
|
||||
|
||||
assert bytearray(b'Is Title Case').istitle()
|
||||
assert not bytearray(b'is Not title casE').istitle()
|
||||
assert bytearray(b"Is Title Case").istitle()
|
||||
assert not bytearray(b"is Not title casE").istitle()
|
||||
|
||||
a = bytearray(b'abcd')
|
||||
# upper lower, capitalize, swapcase
|
||||
l = bytearray(b"lower")
|
||||
b = bytearray(b"UPPER")
|
||||
assert l.lower().islower()
|
||||
assert b.upper().isupper()
|
||||
assert l.capitalize() == b"Lower"
|
||||
assert b.capitalize() == b"Upper"
|
||||
assert bytearray().capitalize() == bytearray()
|
||||
assert b"AaBbCc123'@/".swapcase().swapcase() == b"AaBbCc123'@/"
|
||||
assert b"AaBbCc123'@/".swapcase() == b"aAbBcC123'@/"
|
||||
|
||||
# # hex from hex
|
||||
assert bytearray([0, 1, 9, 23, 90, 234]).hex() == "000109175aea"
|
||||
|
||||
bytearray.fromhex("62 6c7a 34350a ") == b"blz45\n"
|
||||
try:
|
||||
bytearray.fromhex("62 a 21")
|
||||
except ValueError as e:
|
||||
str(e) == "non-hexadecimal number found in fromhex() arg at position 4"
|
||||
try:
|
||||
bytearray.fromhex("6Z2")
|
||||
except ValueError as e:
|
||||
str(e) == "non-hexadecimal number found in fromhex() arg at position 1"
|
||||
with assertRaises(TypeError):
|
||||
bytearray.fromhex(b"hhjjk")
|
||||
# center
|
||||
assert [bytearray(b"koki").center(i, b"|") for i in range(3, 10)] == [
|
||||
b"koki",
|
||||
b"koki",
|
||||
b"|koki",
|
||||
b"|koki|",
|
||||
b"||koki|",
|
||||
b"||koki||",
|
||||
b"|||koki||",
|
||||
]
|
||||
|
||||
assert [bytearray(b"kok").center(i, b"|") for i in range(2, 10)] == [
|
||||
b"kok",
|
||||
b"kok",
|
||||
b"kok|",
|
||||
b"|kok|",
|
||||
b"|kok||",
|
||||
b"||kok||",
|
||||
b"||kok|||",
|
||||
b"|||kok|||",
|
||||
]
|
||||
bytearray(b"kok").center(4) == b" kok" # " test no arg"
|
||||
with assertRaises(TypeError):
|
||||
bytearray(b"b").center(2, "a")
|
||||
with assertRaises(TypeError):
|
||||
bytearray(b"b").center(2, b"ba")
|
||||
with assertRaises(TypeError):
|
||||
bytearray(b"b").center(b"ba")
|
||||
assert bytearray(b"kok").center(5, bytearray(b"x")) == b"xkokx"
|
||||
bytearray(b"kok").center(-5) == b"kok"
|
||||
|
||||
|
||||
# ljust
|
||||
assert [bytearray(b"koki").ljust(i, b"|") for i in range(3, 10)] == [
|
||||
b"koki",
|
||||
b"koki",
|
||||
b"koki|",
|
||||
b"koki||",
|
||||
b"koki|||",
|
||||
b"koki||||",
|
||||
b"koki|||||",
|
||||
]
|
||||
assert [bytearray(b"kok").ljust(i, b"|") for i in range(2, 10)] == [
|
||||
b"kok",
|
||||
b"kok",
|
||||
b"kok|",
|
||||
b"kok||",
|
||||
b"kok|||",
|
||||
b"kok||||",
|
||||
b"kok|||||",
|
||||
b"kok||||||",
|
||||
]
|
||||
|
||||
bytearray(b"kok").ljust(4) == b"kok " # " test no arg"
|
||||
with assertRaises(TypeError):
|
||||
bytearray(b"b").ljust(2, "a")
|
||||
with assertRaises(TypeError):
|
||||
bytearray(b"b").ljust(2, b"ba")
|
||||
with assertRaises(TypeError):
|
||||
bytearray(b"b").ljust(b"ba")
|
||||
assert bytearray(b"kok").ljust(5, bytearray(b"x")) == b"kokxx"
|
||||
assert bytearray(b"kok").ljust(-5) == b"kok"
|
||||
|
||||
# rjust
|
||||
assert [bytearray(b"koki").rjust(i, b"|") for i in range(3, 10)] == [
|
||||
b"koki",
|
||||
b"koki",
|
||||
b"|koki",
|
||||
b"||koki",
|
||||
b"|||koki",
|
||||
b"||||koki",
|
||||
b"|||||koki",
|
||||
]
|
||||
assert [bytearray(b"kok").rjust(i, b"|") for i in range(2, 10)] == [
|
||||
b"kok",
|
||||
b"kok",
|
||||
b"|kok",
|
||||
b"||kok",
|
||||
b"|||kok",
|
||||
b"||||kok",
|
||||
b"|||||kok",
|
||||
b"||||||kok",
|
||||
]
|
||||
|
||||
|
||||
bytearray(b"kok").rjust(4) == b" kok" # " test no arg"
|
||||
with assertRaises(TypeError):
|
||||
bytearray(b"b").rjust(2, "a")
|
||||
with assertRaises(TypeError):
|
||||
bytearray(b"b").rjust(2, b"ba")
|
||||
with assertRaises(TypeError):
|
||||
bytearray(b"b").rjust(b"ba")
|
||||
assert bytearray(b"kok").rjust(5, bytearray(b"x")) == b"xxkok"
|
||||
assert bytearray(b"kok").rjust(-5) == b"kok"
|
||||
|
||||
|
||||
# count
|
||||
assert bytearray(b"azeazerazeazopia").count(b"aze") == 3
|
||||
assert bytearray(b"azeazerazeazopia").count(b"az") == 4
|
||||
assert bytearray(b"azeazerazeazopia").count(b"a") == 5
|
||||
assert bytearray(b"123456789").count(b"") == 10
|
||||
assert bytearray(b"azeazerazeazopia").count(bytearray(b"aze")) == 3
|
||||
assert bytearray(b"azeazerazeazopia").count(memoryview(b"aze")) == 3
|
||||
assert bytearray(b"azeazerazeazopia").count(memoryview(b"aze"), 1, 9) == 1
|
||||
assert bytearray(b"azeazerazeazopia").count(b"aze", None, None) == 3
|
||||
assert bytearray(b"azeazerazeazopia").count(b"aze", 2, None) == 2
|
||||
assert bytearray(b"azeazerazeazopia").count(b"aze", 2) == 2
|
||||
assert bytearray(b"azeazerazeazopia").count(b"aze", None, 7) == 2
|
||||
assert bytearray(b"azeazerazeazopia").count(b"aze", None, 7) == 2
|
||||
assert bytearray(b"azeazerazeazopia").count(b"aze", 2, 7) == 1
|
||||
assert bytearray(b"azeazerazeazopia").count(b"aze", -13, -10) == 1
|
||||
assert bytearray(b"azeazerazeazopia").count(b"aze", 1, 10000) == 2
|
||||
with assertRaises(ValueError):
|
||||
bytearray(b"ilj").count(3550)
|
||||
assert bytearray(b"azeazerazeazopia").count(97) == 5
|
||||
|
||||
# join
|
||||
assert bytearray(b"").join(
|
||||
(b"jiljl", bytearray(b"kmoomk"), memoryview(b"aaaa"))
|
||||
) == bytearray(b"jiljlkmoomkaaaa")
|
||||
with assertRaises(TypeError):
|
||||
bytearray(b"").join((b"km", "kl"))
|
||||
|
||||
|
||||
# endswith startswith
|
||||
assert bytearray(b"abcde").endswith(b"de")
|
||||
assert bytearray(b"abcde").endswith(b"")
|
||||
assert not bytearray(b"abcde").endswith(b"zx")
|
||||
assert bytearray(b"abcde").endswith(b"bc", 0, 3)
|
||||
assert not bytearray(b"abcde").endswith(b"bc", 2, 3)
|
||||
assert bytearray(b"abcde").endswith((b"c", bytearray(b"de")))
|
||||
|
||||
assert bytearray(b"abcde").startswith(b"ab")
|
||||
assert bytearray(b"abcde").startswith(b"")
|
||||
assert not bytearray(b"abcde").startswith(b"zx")
|
||||
assert bytearray(b"abcde").startswith(b"cd", 2)
|
||||
assert not bytearray(b"abcde").startswith(b"cd", 1, 4)
|
||||
assert bytearray(b"abcde").startswith((b"a", bytearray(b"bc")))
|
||||
|
||||
|
||||
# index find
|
||||
assert bytearray(b"abcd").index(b"cd") == 2
|
||||
assert bytearray(b"abcd").index(b"cd", 0) == 2
|
||||
assert bytearray(b"abcd").index(b"cd", 1) == 2
|
||||
assert bytearray(b"abcd").index(99) == 2
|
||||
with assertRaises(ValueError):
|
||||
bytearray(b"abcde").index(b"c", 3, 1)
|
||||
with assertRaises(ValueError):
|
||||
bytearray(b"abcd").index(b"cdaaaaa")
|
||||
with assertRaises(ValueError):
|
||||
bytearray(b"abcd").index(b"b", 3, 4)
|
||||
with assertRaises(ValueError):
|
||||
bytearray(b"abcd").index(1)
|
||||
|
||||
|
||||
assert bytearray(b"abcd").find(b"cd") == 2
|
||||
assert bytearray(b"abcd").find(b"cd", 0) == 2
|
||||
assert bytearray(b"abcd").find(b"cd", 1) == 2
|
||||
assert bytearray(b"abcde").find(b"c", 3, 1) == -1
|
||||
assert bytearray(b"abcd").find(b"cdaaaaa") == -1
|
||||
assert bytearray(b"abcd").find(b"b", 3, 4) == -1
|
||||
assert bytearray(b"abcd").find(1) == -1
|
||||
assert bytearray(b"abcd").find(99) == 2
|
||||
|
||||
assert bytearray(b"abcdabcda").find(b"a") == 0
|
||||
assert bytearray(b"abcdabcda").rfind(b"a") == 8
|
||||
assert bytearray(b"abcdabcda").rfind(b"a", 2, 6) == 4
|
||||
assert bytearray(b"abcdabcda").rfind(b"a", None, 6) == 4
|
||||
assert bytearray(b"abcdabcda").rfind(b"a", 2, None) == 8
|
||||
assert bytearray(b"abcdabcda").index(b"a") == 0
|
||||
assert bytearray(b"abcdabcda").rindex(b"a") == 8
|
||||
|
||||
|
||||
# make trans
|
||||
# fmt: off
|
||||
assert (
|
||||
bytearray.maketrans(memoryview(b"abc"), bytearray(b"zzz"))
|
||||
== bytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 122, 122, 122, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255])
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
# translate
|
||||
assert bytearray(b"hjhtuyjyujuyj").translate(
|
||||
bytearray.maketrans(b"hj", bytearray(b"ab")), bytearray(b"h")
|
||||
) == bytearray(b"btuybyubuyb")
|
||||
assert bytearray(b"hjhtuyjyujuyj").translate(
|
||||
bytearray.maketrans(b"hj", bytearray(b"ab")), bytearray(b"a")
|
||||
) == bytearray(b"abatuybyubuyb")
|
||||
assert bytearray(b"hjhtuyjyujuyj").translate(
|
||||
bytearray.maketrans(b"hj", bytearray(b"ab"))
|
||||
) == bytearray(b"abatuybyubuyb")
|
||||
assert bytearray(b"hjhtuyfjtyhuhjuyj").translate(None, bytearray(b"ht")) == bytearray(
|
||||
b"juyfjyujuyj"
|
||||
)
|
||||
assert bytearray(b"hjhtuyfjtyhuhjuyj").translate(None, delete=b"ht") == bytearray(
|
||||
b"juyfjyujuyj"
|
||||
)
|
||||
|
||||
|
||||
# strip lstrip rstrip
|
||||
assert bytearray(b" spacious ").strip() == bytearray(b"spacious")
|
||||
assert bytearray(b"www.example.com").strip(b"cmowz.") == bytearray(b"example")
|
||||
assert bytearray(b" spacious ").lstrip() == bytearray(b"spacious ")
|
||||
assert bytearray(b"www.example.com").lstrip(b"cmowz.") == bytearray(b"example.com")
|
||||
assert bytearray(b" spacious ").rstrip() == bytearray(b" spacious")
|
||||
assert bytearray(b"mississippi").rstrip(b"ipz") == bytearray(b"mississ")
|
||||
|
||||
|
||||
|
||||
# split
|
||||
assert bytearray(b"1,2,3").split(bytearray(b",")) == [bytearray(b"1"), bytearray(b"2"), bytearray(b"3")]
|
||||
assert bytearray(b"1,2,3").split(bytearray(b","), maxsplit=1) == [bytearray(b"1"), bytearray(b"2,3")]
|
||||
assert bytearray(b"1,2,,3,").split(bytearray(b",")) == [bytearray(b"1"), bytearray(b"2"), bytearray(b""), bytearray(b"3"), bytearray(b"")]
|
||||
assert bytearray(b"1 2 3").split() == [bytearray(b"1"), bytearray(b"2"), bytearray(b"3")]
|
||||
assert bytearray(b"1 2 3").split(maxsplit=1) == [bytearray(b"1"), bytearray(b"2 3")]
|
||||
assert bytearray(b" 1 2 3 ").split() == [bytearray(b"1"), bytearray(b"2"), bytearray(b"3")]
|
||||
assert bytearray(b"k\ruh\nfz e f").split() == [bytearray(b"k"), bytearray(b"uh"), bytearray(b"fz"), bytearray(b"e"), bytearray(b"f")]
|
||||
assert bytearray(b"Two lines\n").split(bytearray(b"\n")) == [bytearray(b"Two lines"), bytearray(b"")]
|
||||
assert bytearray(b"").split() == []
|
||||
assert bytearray(b"").split(bytearray(b"\n")) == [bytearray(b"")]
|
||||
assert bytearray(b"\n").split(bytearray(b"\n")) == [bytearray(b""), bytearray(b"")]
|
||||
|
||||
SPLIT_FIXTURES = [
|
||||
[
|
||||
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
|
||||
[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
|
||||
-1,
|
||||
],
|
||||
[
|
||||
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [1, 2, 3], [1, 2, 3], []],
|
||||
[[1, 2, 3], [1, 2, 3], [1, 2, 3], []],
|
||||
-1,
|
||||
],
|
||||
[
|
||||
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 3],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [1, 2, 3], [1, 2, 3], [3]],
|
||||
[[1, 2, 3], [1, 2, 3], [1, 2, 3], [3]],
|
||||
-1,
|
||||
],
|
||||
[
|
||||
[4, 5, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[], [2, 3], [1, 2, 3], [1, 2, 3]],
|
||||
[[], [2, 3], [1, 2, 3], [1, 2, 3]],
|
||||
-1,
|
||||
],
|
||||
[
|
||||
[1, 4, 5, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[1], [2, 3], [1, 2, 3], [1, 2, 3]],
|
||||
[[1], [2, 3], [1, 2, 3], [1, 2, 3]],
|
||||
-1,
|
||||
],
|
||||
[
|
||||
[1, 2, 3, 4, 5, 4, 5, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [], [], [1, 2, 3], [1, 2, 3]],
|
||||
[[1, 2, 3], [], [], [1, 2, 3], [1, 2, 3]],
|
||||
-1,
|
||||
],
|
||||
# maxsplit
|
||||
[
|
||||
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [1, 2, 3, 4, 5, 1, 2, 3]],
|
||||
[[1, 2, 3, 4, 5, 1, 2, 3], [1, 2, 3]],
|
||||
1,
|
||||
],
|
||||
[
|
||||
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]],
|
||||
[[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3], []],
|
||||
1,
|
||||
],
|
||||
[
|
||||
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 3],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 3]],
|
||||
[[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3], [3]],
|
||||
1,
|
||||
],
|
||||
[
|
||||
[4, 5, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[], [2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3]],
|
||||
[[4, 5, 2, 3, 4, 5, 1, 2, 3], [1, 2, 3]],
|
||||
1,
|
||||
],
|
||||
[
|
||||
[1, 4, 5, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[1], [2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3]],
|
||||
[[1, 4, 5, 2, 3, 4, 5, 1, 2, 3], [1, 2, 3]],
|
||||
1,
|
||||
],
|
||||
[
|
||||
[1, 2, 3, 4, 5, 4, 5, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [], [4, 5, 1, 2, 3, 4, 5, 1, 2, 3]],
|
||||
[[1, 2, 3, 4, 5, 4, 5], [1, 2, 3], [1, 2, 3]],
|
||||
2,
|
||||
],
|
||||
[
|
||||
[13, 13, 13, 117, 104, 10, 102, 122, 32, 101, 102, 9, 9],
|
||||
None,
|
||||
[[117, 104], [102, 122], [101, 102]],
|
||||
[[117, 104], [102, 122], [101, 102]],
|
||||
-1,
|
||||
],
|
||||
[
|
||||
[13, 13, 13, 117, 104, 10, 102, 122, 32, 101, 102, 9, 9],
|
||||
None,
|
||||
[[117, 104, 10, 102, 122, 32, 101, 102, 9, 9]],
|
||||
[[13, 13, 13, 117, 104, 10, 102, 122, 32, 101, 102]],
|
||||
0,
|
||||
],
|
||||
[
|
||||
[13, 13, 13, 117, 104, 10, 102, 122, 32, 101, 102, 9, 9],
|
||||
None,
|
||||
[[117, 104], [102, 122, 32, 101, 102, 9, 9]],
|
||||
[[13, 13, 13, 117, 104, 10, 102, 122], [101, 102]],
|
||||
1,
|
||||
],
|
||||
[
|
||||
[13, 13, 13, 117, 104, 10, 102, 122, 32, 101, 102, 9, 9],
|
||||
None,
|
||||
[[117, 104], [102, 122], [101, 102, 9, 9]],
|
||||
[[13, 13, 13, 117, 104], [102, 122], [101, 102]],
|
||||
2,
|
||||
],
|
||||
[
|
||||
[13, 13, 13, 117, 104, 10, 10, 10, 102, 122, 32, 32, 101, 102, 9, 9],
|
||||
None,
|
||||
[[117, 104], [102, 122], [101, 102]],
|
||||
[[117, 104], [102, 122], [101, 102]],
|
||||
-1,
|
||||
],
|
||||
[[49, 44, 50, 44, 51], [44], [[49], [50], [51]], [[49], [50], [51]], -1],
|
||||
[[49, 44, 50, 44, 51], [44], [[49], [50, 44, 51]], [[49, 44, 50], [51]], 1],
|
||||
[
|
||||
[49, 44, 50, 44, 44, 51, 44],
|
||||
[44],
|
||||
[[49], [50], [], [51], []],
|
||||
[[49], [50], [], [51], []],
|
||||
-1,
|
||||
],
|
||||
[[49, 32, 50, 32, 51], None, [[49], [50], [51]], [[49], [50], [51]], -1],
|
||||
[[49, 32, 50, 32, 51], None, [[49], [50, 32, 51]], [[49, 32, 50], [51]], 1],
|
||||
[
|
||||
[32, 32, 32, 49, 32, 32, 32, 50, 32, 32, 32, 51, 32, 32, 32],
|
||||
None,
|
||||
[[49], [50], [51]],
|
||||
[[49], [50], [51]],
|
||||
-1,
|
||||
],
|
||||
]
|
||||
|
||||
|
||||
# for i in SPLIT_FIXTURES: # for not yet implemented : TypeError: Unsupported method: __next__
|
||||
n_sp = 0
|
||||
while n_sp < len(SPLIT_FIXTURES):
|
||||
i = SPLIT_FIXTURES[n_sp]
|
||||
sep = None if i[1] == None else bytearray(i[1])
|
||||
try:
|
||||
assert bytearray(i[0]).split(sep=sep, maxsplit=i[4]) == [bytearray(j) for j in i[2]]
|
||||
except AssertionError:
|
||||
print(i[0], i[1], i[2])
|
||||
print(
|
||||
"Expected : ", [list(x) for x in bytearray(i[0]).split(sep=sep, maxsplit=i[4])]
|
||||
)
|
||||
break
|
||||
|
||||
try:
|
||||
assert bytearray(i[0]).rsplit(sep=sep, maxsplit=i[4]) == [bytearray(j) for j in i[3]]
|
||||
except AssertionError:
|
||||
print(i[0], i[1], i[2])
|
||||
print(
|
||||
"Expected Rev : ",
|
||||
[list(x) for x in bytearray(i[0]).rsplit(sep=sep, maxsplit=i[4])],
|
||||
)
|
||||
break
|
||||
|
||||
n_sp += 1
|
||||
|
||||
|
||||
# expandtabs
|
||||
a = bytearray(b"\x01\x03\r\x05\t8CYZ\t\x06CYZ\t\x17cba`\n\x12\x13\x14")
|
||||
assert (
|
||||
a.expandtabs() == bytearray(b"\x01\x03\r\x05 8CYZ \x06CYZ \x17cba`\n\x12\x13\x14")
|
||||
)
|
||||
assert a.expandtabs(5) == bytearray(b"\x01\x03\r\x05 8CYZ \x06CYZ \x17cba`\n\x12\x13\x14")
|
||||
assert bytearray(b"01\t012\t0123\t01234").expandtabs() == bytearray(b"01 012 0123 01234")
|
||||
assert bytearray(b"01\t012\t0123\t01234").expandtabs(4) == bytearray(b"01 012 0123 01234")
|
||||
assert bytearray(b"123\t123").expandtabs(-5) == bytearray(b"123123")
|
||||
assert bytearray(b"123\t123").expandtabs(0) == bytearray(b"123123")
|
||||
|
||||
|
||||
# # partition
|
||||
assert bytearray(b"123456789").partition(b"45") == ((b"123"), bytearray(b"45"), bytearray(b"6789"))
|
||||
assert bytearray(b"14523456789").partition(b"45") == ((b"1"), bytearray(b"45"), bytearray(b"23456789"))
|
||||
a = bytearray(b"14523456789").partition(b"45")
|
||||
assert isinstance(a[1], bytearray)
|
||||
a = bytearray(b"14523456789").partition(memoryview(b"45"))
|
||||
assert isinstance(a[1], bytearray)
|
||||
|
||||
# partition
|
||||
assert bytearray(b"123456789").rpartition(bytearray(b"45")) == ((bytearray(b"123")), bytearray(b"45"), bytearray(b"6789"))
|
||||
assert bytearray(b"14523456789").rpartition(bytearray(b"45")) == ((bytearray(b"14523")), bytearray(b"45"), bytearray(b"6789"))
|
||||
a = bytearray(b"14523456789").rpartition(b"45")
|
||||
assert isinstance(a[1], bytearray)
|
||||
a = bytearray(b"14523456789").rpartition(memoryview(b"45"))
|
||||
assert isinstance(a[1], bytearray)
|
||||
|
||||
# splitlines
|
||||
assert bytearray(b"ab c\n\nde fg\rkl\r\n").splitlines() == [bytearray(b"ab c"), bytearray(b""), bytearray(b"de fg"), bytearray(b"kl")]
|
||||
assert bytearray(b"ab c\n\nde fg\rkl\r\n").splitlines(keepends=True) == [
|
||||
bytearray(b"ab c\n"),
|
||||
bytearray(b"\n"),
|
||||
bytearray(b"de fg\r"),
|
||||
bytearray(b"kl\r\n"),
|
||||
]
|
||||
assert bytearray(b"").splitlines() == []
|
||||
assert bytearray(b"One line\n").splitlines() == [b"One line"]
|
||||
|
||||
# zfill
|
||||
|
||||
assert bytearray(b"42").zfill(5) == bytearray(b"00042")
|
||||
assert bytearray(b"-42").zfill(5) == bytearray(b"-0042")
|
||||
assert bytearray(b"42").zfill(1) == bytearray(b"42")
|
||||
assert bytearray(b"42").zfill(-1) == bytearray(b"42")
|
||||
|
||||
# replace
|
||||
assert bytearray(b"123456789123").replace(b"23",b"XX") ==bytearray(b'1XX4567891XX')
|
||||
assert bytearray(b"123456789123").replace(b"23",b"XX", 1) ==bytearray(b'1XX456789123')
|
||||
assert bytearray(b"123456789123").replace(b"23",b"XX", 0) == bytearray(b"123456789123")
|
||||
assert bytearray(b"123456789123").replace(b"23",b"XX", -1) ==bytearray(b'1XX4567891XX')
|
||||
assert bytearray(b"123456789123").replace(b"23", bytearray(b"")) == bytearray(b"14567891")
|
||||
|
||||
|
||||
# clear
|
||||
a = bytearray(b"abcd")
|
||||
a.clear()
|
||||
assert len(a) == 0
|
||||
|
||||
try:
|
||||
bytearray([400])
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
assert False
|
||||
|
||||
b = bytearray(b'test')
|
||||
b = bytearray(b"test")
|
||||
assert len(b) == 4
|
||||
b.pop()
|
||||
assert len(b) == 3
|
||||
@@ -65,3 +617,92 @@ except IndexError:
|
||||
pass
|
||||
else:
|
||||
assert False
|
||||
|
||||
a = bytearray(b"appen")
|
||||
assert len(a) == 5
|
||||
a.append(100)
|
||||
assert a == bytearray(b"append")
|
||||
assert len(a) == 6
|
||||
assert a.pop() == 100
|
||||
|
||||
# title
|
||||
assert bytearray(b"Hello world").title() == bytearray(b"Hello World")
|
||||
assert (
|
||||
bytearray(b"they're bill's friends from the UK").title()
|
||||
== bytearray(b"They'Re Bill'S Friends From The Uk")
|
||||
)
|
||||
|
||||
|
||||
# repeat by multiply
|
||||
a = bytearray(b'abcd')
|
||||
assert a * 0 == bytearray(b'')
|
||||
assert a * -1 == bytearray(b'')
|
||||
assert a * 1 == bytearray(b'abcd')
|
||||
assert a * 3 == bytearray(b'abcdabcdabcd')
|
||||
assert 3 * a == bytearray(b'abcdabcdabcd')
|
||||
|
||||
a = bytearray(b'abcd')
|
||||
a.__imul__(3)
|
||||
assert a == bytearray(b'abcdabcdabcd')
|
||||
a.__imul__(0)
|
||||
assert a == bytearray(b'')
|
||||
|
||||
|
||||
# copy
|
||||
a = bytearray(b"my bytearray")
|
||||
b = a.copy()
|
||||
assert a == b
|
||||
assert a is not b
|
||||
b.append(100)
|
||||
assert a != b
|
||||
|
||||
|
||||
# extend
|
||||
a = bytearray(b"hello,")
|
||||
# any iterable of ints should work
|
||||
a.extend([32, 119, 111, 114])
|
||||
a.extend(b"ld")
|
||||
assert a == bytearray(b"hello, world")
|
||||
|
||||
|
||||
# insert
|
||||
a = bytearray(b"hello, world")
|
||||
a.insert(0, 119)
|
||||
assert a == bytearray(b"whello, world"), a
|
||||
# -1 is not at the end, but one before
|
||||
a.insert(-1, 119)
|
||||
assert a == bytearray(b"whello, worlwd"), a
|
||||
# inserting before the beginning just inserts at the beginning
|
||||
a.insert(-1000, 111)
|
||||
assert a == bytearray(b"owhello, worlwd"), a
|
||||
# inserting after the end just inserts at the end
|
||||
a.insert(1000, 111)
|
||||
assert a == bytearray(b"owhello, worlwdo"), a
|
||||
|
||||
|
||||
# remove
|
||||
a = bytearray(b'abcdabcd')
|
||||
a.remove(99) # the letter c
|
||||
# Only the first is removed
|
||||
assert a == bytearray(b'abdabcd')
|
||||
|
||||
|
||||
# reverse
|
||||
a = bytearray(b'hello, world')
|
||||
a.reverse()
|
||||
assert a == bytearray(b'dlrow ,olleh')
|
||||
|
||||
# __setitem__
|
||||
a = bytearray(b'test')
|
||||
a[0] = 1
|
||||
assert a == bytearray(b'\x01est')
|
||||
with assertRaises(TypeError):
|
||||
a[0] = b'a'
|
||||
with assertRaises(TypeError):
|
||||
a[0] = memoryview(b'a')
|
||||
a[:2] = [0, 9]
|
||||
assert a == bytearray(b'\x00\x09st')
|
||||
a[1:3] = b'test'
|
||||
assert a == bytearray(b'\x00testt')
|
||||
a[:6] = memoryview(b'test')
|
||||
assert a == bytearray(b'test')
|
||||
@@ -0,0 +1,599 @@
|
||||
from testutils import assertRaises
|
||||
|
||||
# new
|
||||
assert bytes([1, 2, 3])
|
||||
assert bytes((1, 2, 3))
|
||||
assert bytes(range(4))
|
||||
assert bytes(3)
|
||||
assert b"bla"
|
||||
assert bytes("bla", "utf8") == bytes("bla", encoding="utf-8") == b"bla"
|
||||
with assertRaises(TypeError):
|
||||
bytes("bla")
|
||||
with assertRaises(TypeError):
|
||||
bytes("bla", encoding=b"jilj")
|
||||
|
||||
assert (
|
||||
b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
|
||||
== bytes(range(0, 256))
|
||||
)
|
||||
assert (
|
||||
b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
|
||||
== bytes(range(0, 256))
|
||||
)
|
||||
assert b"omkmok\Xaa" == bytes([111, 109, 107, 109, 111, 107, 92, 88, 97, 97])
|
||||
|
||||
|
||||
a = b"abcd"
|
||||
b = b"ab"
|
||||
c = b"abcd"
|
||||
|
||||
#
|
||||
# repr
|
||||
assert repr(bytes([0, 1, 2])) == repr(b"\x00\x01\x02")
|
||||
assert repr(
|
||||
bytes([0, 1, 9, 10, 11, 13, 31, 32, 33, 89, 120, 255])
|
||||
== "b'\\x00\\x01\\t\\n\\x0b\\r\\x1f !Yx\\xff'"
|
||||
)
|
||||
assert repr(b"abcd") == "b'abcd'"
|
||||
|
||||
# len
|
||||
assert len(bytes("abcdé", "utf8")) == 6
|
||||
|
||||
# comp
|
||||
assert a == b"abcd"
|
||||
assert a > b
|
||||
assert a >= b
|
||||
assert b < a
|
||||
assert b <= a
|
||||
|
||||
assert b"foobar".__eq__(2) == NotImplemented
|
||||
assert b"foobar".__ne__(2) == NotImplemented
|
||||
assert b"foobar".__gt__(2) == NotImplemented
|
||||
assert b"foobar".__ge__(2) == NotImplemented
|
||||
assert b"foobar".__lt__(2) == NotImplemented
|
||||
assert b"foobar".__le__(2) == NotImplemented
|
||||
|
||||
# hash
|
||||
hash(a) == hash(b"abcd")
|
||||
|
||||
# iter
|
||||
[i for i in b"abcd"] == ["a", "b", "c", "d"]
|
||||
assert list(bytes(3)) == [0, 0, 0]
|
||||
|
||||
# add
|
||||
assert a + b == b"abcdab"
|
||||
|
||||
# contains
|
||||
assert b"ab" in b"abcd"
|
||||
assert b"cd" in b"abcd"
|
||||
assert b"abcd" in b"abcd"
|
||||
assert b"a" in b"abcd"
|
||||
assert b"d" in b"abcd"
|
||||
assert b"dc" not in b"abcd"
|
||||
assert 97 in b"abcd"
|
||||
assert 150 not in b"abcd"
|
||||
with assertRaises(ValueError):
|
||||
350 in b"abcd"
|
||||
|
||||
|
||||
# getitem
|
||||
d = b"abcdefghij"
|
||||
|
||||
assert d[1] == 98
|
||||
assert d[-1] == 106
|
||||
assert d[2:6] == b"cdef"
|
||||
assert d[-6:] == b"efghij"
|
||||
assert d[1:8:2] == b"bdfh"
|
||||
assert d[8:1:-2] == b"igec"
|
||||
|
||||
|
||||
# is_xx methods
|
||||
|
||||
assert bytes(b"1a23").isalnum()
|
||||
assert not bytes(b"1%a23").isalnum()
|
||||
|
||||
assert bytes(b"abc").isalpha()
|
||||
assert not bytes(b"abc1").isalpha()
|
||||
|
||||
# travis doesn't like this
|
||||
# assert bytes(b'xyz').isascii()
|
||||
# assert not bytes([128, 157, 32]).isascii()
|
||||
|
||||
assert bytes(b"1234567890").isdigit()
|
||||
assert not bytes(b"12ab").isdigit()
|
||||
|
||||
l = bytes(b"lower")
|
||||
b = bytes(b"UPPER")
|
||||
|
||||
assert l.islower()
|
||||
assert not l.isupper()
|
||||
assert b.isupper()
|
||||
assert not bytes(b"Super Friends").islower()
|
||||
|
||||
assert bytes(b" \n\t").isspace()
|
||||
assert not bytes(b"\td\n").isspace()
|
||||
|
||||
assert b.isupper()
|
||||
assert not b.islower()
|
||||
assert l.islower()
|
||||
assert not bytes(b"tuPpEr").isupper()
|
||||
|
||||
assert bytes(b"Is Title Case").istitle()
|
||||
assert not bytes(b"is Not title casE").istitle()
|
||||
|
||||
# upper lower, capitalize, swapcase
|
||||
l = bytes(b"lower")
|
||||
b = bytes(b"UPPER")
|
||||
assert l.lower().islower()
|
||||
assert b.upper().isupper()
|
||||
assert l.capitalize() == b"Lower"
|
||||
assert b.capitalize() == b"Upper"
|
||||
assert bytes().capitalize() == bytes()
|
||||
assert b"AaBbCc123'@/".swapcase().swapcase() == b"AaBbCc123'@/"
|
||||
assert b"AaBbCc123'@/".swapcase() == b"aAbBcC123'@/"
|
||||
|
||||
# hex from hex
|
||||
assert bytes([0, 1, 9, 23, 90, 234]).hex() == "000109175aea"
|
||||
|
||||
bytes.fromhex("62 6c7a 34350a ") == b"blz45\n"
|
||||
try:
|
||||
bytes.fromhex("62 a 21")
|
||||
except ValueError as e:
|
||||
str(e) == "non-hexadecimal number found in fromhex() arg at position 4"
|
||||
try:
|
||||
bytes.fromhex("6Z2")
|
||||
except ValueError as e:
|
||||
str(e) == "non-hexadecimal number found in fromhex() arg at position 1"
|
||||
with assertRaises(TypeError):
|
||||
bytes.fromhex(b"hhjjk")
|
||||
# center
|
||||
assert [b"koki".center(i, b"|") for i in range(3, 10)] == [
|
||||
b"koki",
|
||||
b"koki",
|
||||
b"|koki",
|
||||
b"|koki|",
|
||||
b"||koki|",
|
||||
b"||koki||",
|
||||
b"|||koki||",
|
||||
]
|
||||
|
||||
assert [b"kok".center(i, b"|") for i in range(2, 10)] == [
|
||||
b"kok",
|
||||
b"kok",
|
||||
b"kok|",
|
||||
b"|kok|",
|
||||
b"|kok||",
|
||||
b"||kok||",
|
||||
b"||kok|||",
|
||||
b"|||kok|||",
|
||||
]
|
||||
b"kok".center(4) == b" kok" # " test no arg"
|
||||
with assertRaises(TypeError):
|
||||
b"b".center(2, "a")
|
||||
with assertRaises(TypeError):
|
||||
b"b".center(2, b"ba")
|
||||
with assertRaises(TypeError):
|
||||
b"b".center(b"ba")
|
||||
assert b"kok".center(5, bytearray(b"x")) == b"xkokx"
|
||||
b"kok".center(-5) == b"kok"
|
||||
|
||||
|
||||
# ljust
|
||||
assert [b"koki".ljust(i, b"|") for i in range(3, 10)] == [
|
||||
b"koki",
|
||||
b"koki",
|
||||
b"koki|",
|
||||
b"koki||",
|
||||
b"koki|||",
|
||||
b"koki||||",
|
||||
b"koki|||||",
|
||||
]
|
||||
assert [b"kok".ljust(i, b"|") for i in range(2, 10)] == [
|
||||
b"kok",
|
||||
b"kok",
|
||||
b"kok|",
|
||||
b"kok||",
|
||||
b"kok|||",
|
||||
b"kok||||",
|
||||
b"kok|||||",
|
||||
b"kok||||||",
|
||||
]
|
||||
|
||||
b"kok".ljust(4) == b"kok " # " test no arg"
|
||||
with assertRaises(TypeError):
|
||||
b"b".ljust(2, "a")
|
||||
with assertRaises(TypeError):
|
||||
b"b".ljust(2, b"ba")
|
||||
with assertRaises(TypeError):
|
||||
b"b".ljust(b"ba")
|
||||
assert b"kok".ljust(5, bytearray(b"x")) == b"kokxx"
|
||||
assert b"kok".ljust(-5) == b"kok"
|
||||
|
||||
# rjust
|
||||
assert [b"koki".rjust(i, b"|") for i in range(3, 10)] == [
|
||||
b"koki",
|
||||
b"koki",
|
||||
b"|koki",
|
||||
b"||koki",
|
||||
b"|||koki",
|
||||
b"||||koki",
|
||||
b"|||||koki",
|
||||
]
|
||||
assert [b"kok".rjust(i, b"|") for i in range(2, 10)] == [
|
||||
b"kok",
|
||||
b"kok",
|
||||
b"|kok",
|
||||
b"||kok",
|
||||
b"|||kok",
|
||||
b"||||kok",
|
||||
b"|||||kok",
|
||||
b"||||||kok",
|
||||
]
|
||||
|
||||
|
||||
b"kok".rjust(4) == b" kok" # " test no arg"
|
||||
with assertRaises(TypeError):
|
||||
b"b".rjust(2, "a")
|
||||
with assertRaises(TypeError):
|
||||
b"b".rjust(2, b"ba")
|
||||
with assertRaises(TypeError):
|
||||
b"b".rjust(b"ba")
|
||||
assert b"kok".rjust(5, bytearray(b"x")) == b"xxkok"
|
||||
assert b"kok".rjust(-5) == b"kok"
|
||||
|
||||
|
||||
# count
|
||||
assert b"azeazerazeazopia".count(b"aze") == 3
|
||||
assert b"azeazerazeazopia".count(b"az") == 4
|
||||
assert b"azeazerazeazopia".count(b"a") == 5
|
||||
assert b"123456789".count(b"") == 10
|
||||
assert b"azeazerazeazopia".count(bytearray(b"aze")) == 3
|
||||
assert b"azeazerazeazopia".count(memoryview(b"aze")) == 3
|
||||
assert b"azeazerazeazopia".count(memoryview(b"aze"), 1, 9) == 1
|
||||
assert b"azeazerazeazopia".count(b"aze", None, None) == 3
|
||||
assert b"azeazerazeazopia".count(b"aze", 2, None) == 2
|
||||
assert b"azeazerazeazopia".count(b"aze", 2) == 2
|
||||
assert b"azeazerazeazopia".count(b"aze", None, 7) == 2
|
||||
assert b"azeazerazeazopia".count(b"aze", None, 7) == 2
|
||||
assert b"azeazerazeazopia".count(b"aze", 2, 7) == 1
|
||||
assert b"azeazerazeazopia".count(b"aze", -13, -10) == 1
|
||||
assert b"azeazerazeazopia".count(b"aze", 1, 10000) == 2
|
||||
with assertRaises(ValueError):
|
||||
b"ilj".count(3550)
|
||||
assert b"azeazerazeazopia".count(97) == 5
|
||||
|
||||
# join
|
||||
assert (
|
||||
b"".join((b"jiljl", bytearray(b"kmoomk"), memoryview(b"aaaa")))
|
||||
== b"jiljlkmoomkaaaa"
|
||||
)
|
||||
with assertRaises(TypeError):
|
||||
b"".join((b"km", "kl"))
|
||||
|
||||
|
||||
# endswith startswith
|
||||
assert b"abcde".endswith(b"de")
|
||||
assert b"abcde".endswith(b"")
|
||||
assert not b"abcde".endswith(b"zx")
|
||||
assert b"abcde".endswith(b"bc", 0, 3)
|
||||
assert not b"abcde".endswith(b"bc", 2, 3)
|
||||
assert b"abcde".endswith((b"c", b"de"))
|
||||
|
||||
assert b"abcde".startswith(b"ab")
|
||||
assert b"abcde".startswith(b"")
|
||||
assert not b"abcde".startswith(b"zx")
|
||||
assert b"abcde".startswith(b"cd", 2)
|
||||
assert not b"abcde".startswith(b"cd", 1, 4)
|
||||
assert b"abcde".startswith((b"a", b"bc"))
|
||||
|
||||
|
||||
# index find
|
||||
assert b"abcd".index(b"cd") == 2
|
||||
assert b"abcd".index(b"cd", 0) == 2
|
||||
assert b"abcd".index(b"cd", 1) == 2
|
||||
assert b"abcd".index(99) == 2
|
||||
with assertRaises(ValueError):
|
||||
b"abcde".index(b"c", 3, 1)
|
||||
with assertRaises(ValueError):
|
||||
b"abcd".index(b"cdaaaaa")
|
||||
with assertRaises(ValueError):
|
||||
b"abcd".index(b"b", 3, 4)
|
||||
with assertRaises(ValueError):
|
||||
b"abcd".index(1)
|
||||
|
||||
|
||||
assert b"abcd".find(b"cd") == 2
|
||||
assert b"abcd".find(b"cd", 0) == 2
|
||||
assert b"abcd".find(b"cd", 1) == 2
|
||||
assert b"abcde".find(b"c", 3, 1) == -1
|
||||
assert b"abcd".find(b"cdaaaaa") == -1
|
||||
assert b"abcd".find(b"b", 3, 4) == -1
|
||||
assert b"abcd".find(1) == -1
|
||||
assert b"abcd".find(99) == 2
|
||||
|
||||
assert b"abcdabcda".find(b"a") == 0
|
||||
assert b"abcdabcda".rfind(b"a") == 8
|
||||
assert b"abcdabcda".rfind(b"a", 2, 6) == 4
|
||||
assert b"abcdabcda".rfind(b"a", None, 6) == 4
|
||||
assert b"abcdabcda".rfind(b"a", 2, None) == 8
|
||||
assert b"abcdabcda".index(b"a") == 0
|
||||
assert b"abcdabcda".rindex(b"a") == 8
|
||||
|
||||
|
||||
# make trans
|
||||
# fmt: off
|
||||
assert (
|
||||
bytes.maketrans(memoryview(b"abc"), bytearray(b"zzz"))
|
||||
== bytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 122, 122, 122, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255])
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
# translate
|
||||
assert b"hjhtuyjyujuyj".translate(bytes.maketrans(b"hj", b"ab"), b"h") == b"btuybyubuyb"
|
||||
assert (
|
||||
b"hjhtuyjyujuyj".translate(bytes.maketrans(b"hj", b"ab"), b"a") == b"abatuybyubuyb"
|
||||
)
|
||||
assert b"hjhtuyjyujuyj".translate(bytes.maketrans(b"hj", b"ab")) == b"abatuybyubuyb"
|
||||
assert b"hjhtuyfjtyhuhjuyj".translate(None, b"ht") == b"juyfjyujuyj"
|
||||
assert b"hjhtuyfjtyhuhjuyj".translate(None, delete=b"ht") == b"juyfjyujuyj"
|
||||
|
||||
|
||||
# strip lstrip rstrip
|
||||
assert b" spacious ".strip() == b"spacious"
|
||||
assert b"www.example.com".strip(b"cmowz.") == b"example"
|
||||
assert b" spacious ".lstrip() == b"spacious "
|
||||
assert b"www.example.com".lstrip(b"cmowz.") == b"example.com"
|
||||
assert b" spacious ".rstrip() == b" spacious"
|
||||
assert b"mississippi".rstrip(b"ipz") == b"mississ"
|
||||
|
||||
|
||||
# split
|
||||
assert b"1,2,3".split(b",") == [b"1", b"2", b"3"]
|
||||
assert b"1,2,3".split(b",", maxsplit=1) == [b"1", b"2,3"]
|
||||
assert b"1,2,,3,".split(b",") == [b"1", b"2", b"", b"3", b""]
|
||||
assert b"1 2 3".split() == [b"1", b"2", b"3"]
|
||||
assert b"1 2 3".split(maxsplit=1) == [b"1", b"2 3"]
|
||||
assert b" 1 2 3 ".split() == [b"1", b"2", b"3"]
|
||||
assert b"k\ruh\nfz e f".split() == [b"k", b"uh", b"fz", b"e", b"f"]
|
||||
assert b"Two lines\n".split(b"\n") == [b"Two lines", b""]
|
||||
assert b"".split() == []
|
||||
assert b"".split(b"\n") == [b""]
|
||||
assert b"\n".split(b"\n") == [b"", b""]
|
||||
|
||||
SPLIT_FIXTURES = [
|
||||
[
|
||||
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
|
||||
[[1, 2, 3], [1, 2, 3], [1, 2, 3]],
|
||||
-1,
|
||||
],
|
||||
[
|
||||
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [1, 2, 3], [1, 2, 3], []],
|
||||
[[1, 2, 3], [1, 2, 3], [1, 2, 3], []],
|
||||
-1,
|
||||
],
|
||||
[
|
||||
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 3],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [1, 2, 3], [1, 2, 3], [3]],
|
||||
[[1, 2, 3], [1, 2, 3], [1, 2, 3], [3]],
|
||||
-1,
|
||||
],
|
||||
[
|
||||
[4, 5, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[], [2, 3], [1, 2, 3], [1, 2, 3]],
|
||||
[[], [2, 3], [1, 2, 3], [1, 2, 3]],
|
||||
-1,
|
||||
],
|
||||
[
|
||||
[1, 4, 5, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[1], [2, 3], [1, 2, 3], [1, 2, 3]],
|
||||
[[1], [2, 3], [1, 2, 3], [1, 2, 3]],
|
||||
-1,
|
||||
],
|
||||
[
|
||||
[1, 2, 3, 4, 5, 4, 5, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [], [], [1, 2, 3], [1, 2, 3]],
|
||||
[[1, 2, 3], [], [], [1, 2, 3], [1, 2, 3]],
|
||||
-1,
|
||||
],
|
||||
# maxsplit
|
||||
[
|
||||
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [1, 2, 3, 4, 5, 1, 2, 3]],
|
||||
[[1, 2, 3, 4, 5, 1, 2, 3], [1, 2, 3]],
|
||||
1,
|
||||
],
|
||||
[
|
||||
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]],
|
||||
[[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3], []],
|
||||
1,
|
||||
],
|
||||
[
|
||||
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 3],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 3]],
|
||||
[[1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3], [3]],
|
||||
1,
|
||||
],
|
||||
[
|
||||
[4, 5, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[], [2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3]],
|
||||
[[4, 5, 2, 3, 4, 5, 1, 2, 3], [1, 2, 3]],
|
||||
1,
|
||||
],
|
||||
[
|
||||
[1, 4, 5, 2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[1], [2, 3, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3]],
|
||||
[[1, 4, 5, 2, 3, 4, 5, 1, 2, 3], [1, 2, 3]],
|
||||
1,
|
||||
],
|
||||
[
|
||||
[1, 2, 3, 4, 5, 4, 5, 4, 5, 1, 2, 3, 4, 5, 1, 2, 3],
|
||||
[4, 5],
|
||||
[[1, 2, 3], [], [4, 5, 1, 2, 3, 4, 5, 1, 2, 3]],
|
||||
[[1, 2, 3, 4, 5, 4, 5], [1, 2, 3], [1, 2, 3]],
|
||||
2,
|
||||
],
|
||||
[
|
||||
[13, 13, 13, 117, 104, 10, 102, 122, 32, 101, 102, 9, 9],
|
||||
None,
|
||||
[[117, 104], [102, 122], [101, 102]],
|
||||
[[117, 104], [102, 122], [101, 102]],
|
||||
-1,
|
||||
],
|
||||
[
|
||||
[13, 13, 13, 117, 104, 10, 102, 122, 32, 101, 102, 9, 9],
|
||||
None,
|
||||
[[117, 104, 10, 102, 122, 32, 101, 102, 9, 9]],
|
||||
[[13, 13, 13, 117, 104, 10, 102, 122, 32, 101, 102]],
|
||||
0,
|
||||
],
|
||||
[
|
||||
[13, 13, 13, 117, 104, 10, 102, 122, 32, 101, 102, 9, 9],
|
||||
None,
|
||||
[[117, 104], [102, 122, 32, 101, 102, 9, 9]],
|
||||
[[13, 13, 13, 117, 104, 10, 102, 122], [101, 102]],
|
||||
1,
|
||||
],
|
||||
[
|
||||
[13, 13, 13, 117, 104, 10, 102, 122, 32, 101, 102, 9, 9],
|
||||
None,
|
||||
[[117, 104], [102, 122], [101, 102, 9, 9]],
|
||||
[[13, 13, 13, 117, 104], [102, 122], [101, 102]],
|
||||
2,
|
||||
],
|
||||
[
|
||||
[13, 13, 13, 117, 104, 10, 10, 10, 102, 122, 32, 32, 101, 102, 9, 9],
|
||||
None,
|
||||
[[117, 104], [102, 122], [101, 102]],
|
||||
[[117, 104], [102, 122], [101, 102]],
|
||||
-1,
|
||||
],
|
||||
[[49, 44, 50, 44, 51], [44], [[49], [50], [51]], [[49], [50], [51]], -1],
|
||||
[[49, 44, 50, 44, 51], [44], [[49], [50, 44, 51]], [[49, 44, 50], [51]], 1],
|
||||
[
|
||||
[49, 44, 50, 44, 44, 51, 44],
|
||||
[44],
|
||||
[[49], [50], [], [51], []],
|
||||
[[49], [50], [], [51], []],
|
||||
-1,
|
||||
],
|
||||
[[49, 32, 50, 32, 51], None, [[49], [50], [51]], [[49], [50], [51]], -1],
|
||||
[[49, 32, 50, 32, 51], None, [[49], [50, 32, 51]], [[49, 32, 50], [51]], 1],
|
||||
[
|
||||
[32, 32, 32, 49, 32, 32, 32, 50, 32, 32, 32, 51, 32, 32, 32],
|
||||
None,
|
||||
[[49], [50], [51]],
|
||||
[[49], [50], [51]],
|
||||
-1,
|
||||
],
|
||||
]
|
||||
|
||||
|
||||
# for i in SPLIT_FIXTURES: # for not yet implemented : TypeError: Unsupported method: __next__
|
||||
n_sp = 0
|
||||
while n_sp < len(SPLIT_FIXTURES):
|
||||
i = SPLIT_FIXTURES[n_sp]
|
||||
sep = None if i[1] == None else bytes(i[1])
|
||||
try:
|
||||
assert bytes(i[0]).split(sep=sep, maxsplit=i[4]) == [bytes(j) for j in i[2]]
|
||||
except AssertionError:
|
||||
print(i[0], i[1], i[2])
|
||||
print(
|
||||
"Expected : ", [list(x) for x in bytes(i[0]).split(sep=sep, maxsplit=i[4])]
|
||||
)
|
||||
break
|
||||
|
||||
try:
|
||||
assert bytes(i[0]).rsplit(sep=sep, maxsplit=i[4]) == [bytes(j) for j in i[3]]
|
||||
except AssertionError:
|
||||
print(i[0], i[1], i[2])
|
||||
print(
|
||||
"Expected Rev : ",
|
||||
[list(x) for x in bytes(i[0]).rsplit(sep=sep, maxsplit=i[4])],
|
||||
)
|
||||
break
|
||||
|
||||
n_sp += 1
|
||||
|
||||
|
||||
# expandtabs
|
||||
a = b"\x01\x03\r\x05\t8CYZ\t\x06CYZ\t\x17cba`\n\x12\x13\x14"
|
||||
assert (
|
||||
a.expandtabs() == b"\x01\x03\r\x05 8CYZ \x06CYZ \x17cba`\n\x12\x13\x14"
|
||||
)
|
||||
assert a.expandtabs(5) == b"\x01\x03\r\x05 8CYZ \x06CYZ \x17cba`\n\x12\x13\x14"
|
||||
assert b"01\t012\t0123\t01234".expandtabs() == b"01 012 0123 01234"
|
||||
assert b"01\t012\t0123\t01234".expandtabs(4) == b"01 012 0123 01234"
|
||||
assert b"123\t123".expandtabs(-5) == b"123123"
|
||||
assert b"123\t123".expandtabs(0) == b"123123"
|
||||
|
||||
|
||||
# partition
|
||||
assert b"123456789".partition(b"45") == (b"123", b"45", b"6789")
|
||||
assert b"14523456789".partition(b"45") == (b"1", b"45", b"23456789")
|
||||
a = b"14523456789".partition(bytearray(b"45"))
|
||||
assert isinstance(a[1], bytearray)
|
||||
a = b"14523456789".partition(memoryview(b"45"))
|
||||
assert isinstance(a[1], memoryview)
|
||||
|
||||
# partition
|
||||
assert b"123456789".rpartition(b"45") == (b"123", b"45", b"6789")
|
||||
assert b"14523456789".rpartition(b"45") == (b"14523", b"45", b"6789")
|
||||
a = b"14523456789".rpartition(bytearray(b"45"))
|
||||
assert isinstance(a[1], bytearray)
|
||||
a = b"14523456789".rpartition(memoryview(b"45"))
|
||||
assert isinstance(a[1], memoryview)
|
||||
|
||||
# splitlines
|
||||
assert b"ab c\n\nde fg\rkl\r\n".splitlines() == [b"ab c", b"", b"de fg", b"kl"]
|
||||
assert b"ab c\n\nde fg\rkl\r\n".splitlines(keepends=True) == [
|
||||
b"ab c\n",
|
||||
b"\n",
|
||||
b"de fg\r",
|
||||
b"kl\r\n",
|
||||
]
|
||||
assert b"".splitlines() == []
|
||||
assert b"One line\n".splitlines() == [b"One line"]
|
||||
|
||||
# zfill
|
||||
|
||||
assert b"42".zfill(5) == b"00042"
|
||||
assert b"-42".zfill(5) == b"-0042"
|
||||
assert b"42".zfill(1) == b"42"
|
||||
assert b"42".zfill(-1) == b"42"
|
||||
|
||||
# replace
|
||||
assert b"123456789123".replace(b"23", b"XX") == b"1XX4567891XX"
|
||||
assert b"123456789123".replace(b"23", b"XX", 1) == b"1XX456789123"
|
||||
assert b"123456789123".replace(b"23", b"XX", 0) == b"123456789123"
|
||||
assert b"123456789123".replace(b"23", b"XX", -1) == b"1XX4567891XX"
|
||||
assert b"123456789123".replace(b"23", b"") == b"14567891"
|
||||
|
||||
# title
|
||||
assert b"Hello world".title() == b"Hello World"
|
||||
assert (
|
||||
b"they're bill's friends from the UK".title()
|
||||
== b"They'Re Bill'S Friends From The Uk"
|
||||
)
|
||||
|
||||
|
||||
# repeat by multiply
|
||||
a = b'abcd'
|
||||
assert a * 0 == b''
|
||||
assert a * -1 == b''
|
||||
assert a * 1 == b'abcd'
|
||||
assert a * 3 == b'abcdabcdabcd'
|
||||
assert 3 * a == b'abcdabcdabcd'
|
||||
+41
-3
@@ -1,3 +1,6 @@
|
||||
__name__ = "class"
|
||||
|
||||
|
||||
class Foo:
|
||||
def __init__(self, x):
|
||||
assert x == 5
|
||||
@@ -14,7 +17,12 @@ foo = Foo(5)
|
||||
assert foo.y == Foo.y
|
||||
assert foo.x == 5
|
||||
assert foo.square() == 25
|
||||
|
||||
assert Foo.__name__ == "Foo"
|
||||
assert Foo.__qualname__ == "Foo"
|
||||
assert Foo.__module__ == "class"
|
||||
assert Foo.square.__name__ == "square"
|
||||
assert Foo.square.__qualname__ == "Foo.square"
|
||||
assert Foo.square.__module__ == "class"
|
||||
|
||||
class Bar:
|
||||
""" W00t """
|
||||
@@ -37,8 +45,7 @@ class Bar:
|
||||
assert x == 3
|
||||
|
||||
|
||||
# TODO:
|
||||
# assert Bar.__doc__ == " W00t "
|
||||
assert Bar.__doc__ == " W00t "
|
||||
|
||||
bar = Bar(42)
|
||||
|
||||
@@ -117,3 +124,34 @@ assert isinstance(a, super)
|
||||
assert type(a) is super
|
||||
assert a.conjugate() == 1
|
||||
|
||||
|
||||
class T1:
|
||||
"test1"
|
||||
|
||||
assert T1.__doc__ == "test1"
|
||||
|
||||
class T2:
|
||||
'''test2'''
|
||||
|
||||
assert T2.__doc__ == "test2"
|
||||
|
||||
class T3:
|
||||
"""
|
||||
test3
|
||||
"""
|
||||
|
||||
assert T3.__doc__ == "\n test3\n "
|
||||
|
||||
class T4:
|
||||
|
||||
"""test4"""
|
||||
|
||||
def t1(self):
|
||||
"""t1"""
|
||||
pass
|
||||
|
||||
assert T4.__doc__ == "test4"
|
||||
assert T4.t1.__doc__ == "t1"
|
||||
|
||||
cm = classmethod(lambda cls: cls)
|
||||
assert cm.__func__(int) is int
|
||||
@@ -0,0 +1,23 @@
|
||||
from testutils import assert_raises
|
||||
|
||||
assert 1 < 2
|
||||
assert 1 < 2 < 3
|
||||
assert 5 == 5 == 5
|
||||
assert (5 == 5) == True
|
||||
assert 5 == 5 != 4 == 4 > 3 > 2 < 3 <= 3 != 0 == 0
|
||||
|
||||
assert not 1 > 2
|
||||
assert not 5 == 5 == True
|
||||
assert not 5 == 5 != 5 == 5
|
||||
assert not 1 < 2 < 3 > 4
|
||||
assert not 1 < 2 > 3 < 4
|
||||
assert not 1 > 2 < 3 < 4
|
||||
|
||||
def test_type_error(x, y):
|
||||
assert_raises(TypeError, lambda: x < y)
|
||||
assert_raises(TypeError, lambda: x <= y)
|
||||
assert_raises(TypeError, lambda: x > y)
|
||||
assert_raises(TypeError, lambda: x >= y)
|
||||
|
||||
test_type_error([], 0)
|
||||
test_type_error((), 0)
|
||||
@@ -22,3 +22,8 @@ assert u['1'] == -1
|
||||
y = [a+2 for a in x if a % 2]
|
||||
print(y)
|
||||
assert y == [3, 5]
|
||||
|
||||
z = [(9,), (10,)]
|
||||
w = [x for x, in z]
|
||||
assert w == [9, 10]
|
||||
|
||||
@@ -15,6 +15,16 @@ c = add(10, 3)
|
||||
assert c == 14
|
||||
|
||||
|
||||
@logged
|
||||
def add3(a, b, c=2):
|
||||
return a + b + c
|
||||
|
||||
|
||||
d = add3(12, 5)
|
||||
|
||||
assert d == 20
|
||||
|
||||
|
||||
def f(func): return lambda: 42
|
||||
class A: pass
|
||||
a = A()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from testutils import assert_raises, assertRaises
|
||||
|
||||
a = 1
|
||||
del a
|
||||
@@ -10,3 +11,11 @@ del foo.bar
|
||||
|
||||
assert not hasattr(foo, 'bar')
|
||||
|
||||
x = 1
|
||||
y = 2
|
||||
del (x, y)
|
||||
assert_raises(NameError, lambda: x)
|
||||
assert_raises(NameError, lambda: y)
|
||||
|
||||
with assertRaises(NameError):
|
||||
del y
|
||||
+195
-12
@@ -1,19 +1,26 @@
|
||||
def dict_eq(d1, d2):
|
||||
return (all(k in d2 and d1[k] == d2[k] for k in d1)
|
||||
and all(k in d1 and d1[k] == d2[k] for k in d2))
|
||||
from testutils import assertRaises
|
||||
|
||||
assert dict(a=2, b=3) == {'a': 2, 'b': 3}
|
||||
assert dict({'a': 2, 'b': 3}, b=4) == {'a': 2, 'b': 4}
|
||||
assert dict([('a', 2), ('b', 3)]) == {'a': 2, 'b': 3}
|
||||
|
||||
assert dict_eq(dict(a=2, b=3), {'a': 2, 'b': 3})
|
||||
assert dict_eq(dict({'a': 2, 'b': 3}, b=4), {'a': 2, 'b': 4})
|
||||
assert dict_eq(dict([('a', 2), ('b', 3)]), {'a': 2, 'b': 3})
|
||||
assert {} == {}
|
||||
assert not {'a': 2} == {}
|
||||
assert not {} == {'a': 2}
|
||||
assert not {'b': 2} == {'a': 2}
|
||||
assert not {'a': 4} == {'a': 2}
|
||||
assert {'a': 2} == {'a': 2}
|
||||
|
||||
nan = float('nan')
|
||||
assert {'a': nan} == {'a': nan}
|
||||
|
||||
a = {'g': 5}
|
||||
b = {'a': a, 'd': 9}
|
||||
c = dict(b)
|
||||
c['d'] = 3
|
||||
c['a']['g'] = 2
|
||||
assert dict_eq(a, {'g': 2})
|
||||
assert dict_eq(b, {'a': a, 'd': 9})
|
||||
assert a == {'g': 2}
|
||||
assert b == {'a': a, 'd': 9}
|
||||
|
||||
a.clear()
|
||||
assert len(a) == 0
|
||||
@@ -21,16 +28,192 @@ assert len(a) == 0
|
||||
a = {'a': 5, 'b': 6}
|
||||
res = set()
|
||||
for value in a.values():
|
||||
res.add(value)
|
||||
res.add(value)
|
||||
assert res == set([5,6])
|
||||
|
||||
count = 0
|
||||
for (key, value) in a.items():
|
||||
assert a[key] == value
|
||||
count += 1
|
||||
assert a[key] == value
|
||||
count += 1
|
||||
assert count == len(a)
|
||||
|
||||
res = set()
|
||||
for key in a.keys():
|
||||
res.add(key)
|
||||
res.add(key)
|
||||
assert res == set(['a','b'])
|
||||
|
||||
# Deleted values are correctly skipped over:
|
||||
x = {'a': 1, 'b': 2, 'c': 3, 'd': 3}
|
||||
del x['c']
|
||||
it = iter(x.items())
|
||||
assert ('a', 1) == next(it)
|
||||
assert ('b', 2) == next(it)
|
||||
assert ('d', 3) == next(it)
|
||||
with assertRaises(StopIteration):
|
||||
next(it)
|
||||
|
||||
# Iterating a dictionary is just its keys:
|
||||
assert ['a', 'b', 'd'] == list(x)
|
||||
|
||||
# Iterating view captures dictionary when iterated.
|
||||
data = {1: 2, 3: 4}
|
||||
items = data.items()
|
||||
assert list(items) == [(1, 2), (3, 4)]
|
||||
data[5] = 6
|
||||
assert list(items) == [(1, 2), (3, 4), (5, 6)]
|
||||
|
||||
# Values can be changed during iteration.
|
||||
data = {1: 2, 3: 4}
|
||||
items = iter(data.items())
|
||||
assert (1, 2) == next(items)
|
||||
data[3] = "changed"
|
||||
assert (3, "changed") == next(items)
|
||||
|
||||
# But we can't add or delete items during iteration.
|
||||
d = {}
|
||||
a = iter(d.items())
|
||||
d['a'] = 2
|
||||
b = iter(d.items())
|
||||
assert ('a', 2) == next(b)
|
||||
with assertRaises(RuntimeError):
|
||||
next(a)
|
||||
del d['a']
|
||||
with assertRaises(RuntimeError):
|
||||
next(b)
|
||||
|
||||
# View isn't itself an iterator.
|
||||
with assertRaises(TypeError):
|
||||
next(data.keys())
|
||||
|
||||
assert len(data.keys()) == 2
|
||||
|
||||
x = {}
|
||||
x[1] = 1
|
||||
assert x[1] == 1
|
||||
|
||||
x[7] = 7
|
||||
x[2] = 2
|
||||
x[(5, 6)] = 5
|
||||
|
||||
with assertRaises(KeyError):
|
||||
x["not here"]
|
||||
|
||||
with assertRaises(TypeError):
|
||||
x[[]] # Unhashable type.
|
||||
|
||||
x["here"] = "here"
|
||||
assert x.get("not here", "default") == "default"
|
||||
assert x.get("here", "default") == "here"
|
||||
assert x.get("not here") == None
|
||||
|
||||
class LengthDict(dict):
|
||||
def __getitem__(self, k):
|
||||
return len(k)
|
||||
|
||||
x = LengthDict()
|
||||
assert type(x) == LengthDict
|
||||
assert x['word'] == 4
|
||||
assert x.get('word') is None
|
||||
|
||||
assert 5 == eval("a + word", LengthDict())
|
||||
|
||||
|
||||
class Squares(dict):
|
||||
def __missing__(self, k):
|
||||
v = k * k
|
||||
self[k] = v
|
||||
return v
|
||||
|
||||
x = Squares()
|
||||
assert x[-5] == 25
|
||||
|
||||
# An object that hashes to the same value always, and compares equal if any its values match.
|
||||
class Hashable(object):
|
||||
def __init__(self, *args):
|
||||
self.values = args
|
||||
def __hash__(self):
|
||||
return 1
|
||||
def __eq__(self, other):
|
||||
for x in self.values:
|
||||
for y in other.values:
|
||||
if x == y:
|
||||
return True
|
||||
return False
|
||||
|
||||
x = {}
|
||||
x[Hashable(1,2)] = 8
|
||||
|
||||
assert x[Hashable(1,2)] == 8
|
||||
assert x[Hashable(3,1)] == 8
|
||||
|
||||
x[Hashable(8)] = 19
|
||||
x[Hashable(19,8)] = 1
|
||||
assert x[Hashable(8)] == 1
|
||||
assert len(x) == 2
|
||||
|
||||
assert list({'a': 2, 'b': 10}) == ['a', 'b']
|
||||
x = {}
|
||||
x['a'] = 2
|
||||
x['b'] = 10
|
||||
assert list(x) == ['a', 'b']
|
||||
|
||||
y = x.copy()
|
||||
x['c'] = 12
|
||||
assert y == {'a': 2, 'b': 10}
|
||||
|
||||
y.update({'c': 19, "d": -1, 'b': 12})
|
||||
assert y == {'a': 2, 'b': 12, 'c': 19, 'd': -1}
|
||||
|
||||
y.update(y)
|
||||
assert y == {'a': 2, 'b': 12, 'c': 19, 'd': -1} # hasn't changed
|
||||
|
||||
x = {1: 'a', '1': None}
|
||||
assert x.pop(1) == 'a'
|
||||
assert x.pop('1') is None
|
||||
assert x == {}
|
||||
|
||||
with assertRaises(KeyError):
|
||||
x.pop("not here")
|
||||
|
||||
x = {1: 'a'}
|
||||
assert (1, 'a') == x.popitem()
|
||||
with assertRaises(KeyError):
|
||||
x.popitem()
|
||||
assert x == {}
|
||||
|
||||
x = {'a': 4}
|
||||
assert 4 == x.setdefault('a', 0)
|
||||
assert x['a'] == 4
|
||||
assert 0 == x.setdefault('b', 0)
|
||||
assert x['b'] == 0
|
||||
assert None == x.setdefault('c')
|
||||
assert x['c'] is None
|
||||
|
||||
assert {1: None, "b": None} == dict.fromkeys([1, "b"])
|
||||
assert {1: 0, "b": 0} == dict.fromkeys([1, "b"], 0)
|
||||
|
||||
x = {'a': 1, 'b': 1, 'c': 1}
|
||||
y = {'b': 2, 'c': 2, 'd': 2}
|
||||
z = {'c': 3, 'd': 3, 'e': 3}
|
||||
|
||||
w = {1: 1, **x, 2: 2, **y, 3: 3, **z, 4: 4}
|
||||
assert w == {1: 1, 'a': 1, 'b': 2, 'c': 3, 2: 2, 'd': 3, 3: 3, 'e': 3, 4: 4}
|
||||
|
||||
assert str({True: True, 1.0: 1.0}) == str({True: 1.0})
|
||||
|
||||
class A:
|
||||
def __hash__(self):
|
||||
return 1
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, A)
|
||||
class B:
|
||||
def __hash__(self):
|
||||
return 1
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, B)
|
||||
|
||||
s = {1: 0, A(): 1, B(): 2}
|
||||
assert len(s) == 3
|
||||
assert s[1] == 0
|
||||
assert s[A()] == 1
|
||||
assert s[B()] == 2
|
||||
@@ -0,0 +1 @@
|
||||
print('Hello')
|
||||
@@ -7,10 +7,16 @@ from testutils import assert_raises
|
||||
a = 1.2
|
||||
b = 1.3
|
||||
c = 1.2
|
||||
z = 2
|
||||
ov = 10 ** 1000
|
||||
|
||||
assert -a == -1.2
|
||||
|
||||
assert a < b
|
||||
assert not b < a
|
||||
assert a <= b
|
||||
assert a <= c
|
||||
assert a < z
|
||||
|
||||
assert b > a
|
||||
assert not a > b
|
||||
@@ -18,13 +24,39 @@ assert not a > c
|
||||
assert b >= a
|
||||
assert c >= a
|
||||
assert not a >= b
|
||||
assert z > a
|
||||
|
||||
assert a + b == 2.5
|
||||
assert a - c == 0
|
||||
assert a / c == 1
|
||||
assert a % c == 0
|
||||
assert a + z == 3.2
|
||||
assert z + a == 3.2
|
||||
assert a - z == -0.8
|
||||
assert z - a == 0.8
|
||||
assert a / z == 0.6
|
||||
assert 6 / a == 5.0
|
||||
assert 2.0 % z == 0.0
|
||||
assert z % 2.0 == 0.0
|
||||
assert_raises(OverflowError, lambda: a + ov)
|
||||
assert_raises(OverflowError, lambda: a - ov)
|
||||
assert_raises(OverflowError, lambda: a * ov)
|
||||
assert_raises(OverflowError, lambda: a / ov)
|
||||
assert_raises(OverflowError, lambda: a // ov)
|
||||
assert_raises(OverflowError, lambda: a % ov)
|
||||
assert_raises(OverflowError, lambda: a ** ov)
|
||||
assert_raises(OverflowError, lambda: ov + a)
|
||||
assert_raises(OverflowError, lambda: ov - a)
|
||||
assert_raises(OverflowError, lambda: ov * a)
|
||||
assert_raises(OverflowError, lambda: ov / a)
|
||||
assert_raises(OverflowError, lambda: ov // a)
|
||||
assert_raises(OverflowError, lambda: ov % a)
|
||||
# assert_raises(OverflowError, lambda: ov ** a)
|
||||
|
||||
assert a < 5
|
||||
assert a <= 5
|
||||
assert a < 5.5
|
||||
assert a <= 5.5
|
||||
try:
|
||||
assert a < 'a'
|
||||
except TypeError:
|
||||
@@ -66,6 +98,21 @@ assert float(b'2.99e-23') == 2.99e-23
|
||||
assert_raises(ValueError, lambda: float('foo'))
|
||||
assert_raises(OverflowError, lambda: float(2**10000))
|
||||
|
||||
# check eq and hash for small numbers
|
||||
|
||||
assert 1.0 == 1
|
||||
assert 1.0 == True
|
||||
assert 0.0 == 0
|
||||
assert 0.0 == False
|
||||
assert hash(1.0) == hash(1)
|
||||
assert hash(1.0) == hash(True)
|
||||
assert hash(0.0) == hash(0)
|
||||
assert hash(0.0) == hash(False)
|
||||
assert hash(1.0) != hash(1.0000000001)
|
||||
|
||||
assert 5.0 in {3, 4, 5}
|
||||
assert {-1: 2}[-1.0] == 2
|
||||
|
||||
# check that magic methods are implemented for ints and floats
|
||||
|
||||
assert 1.0.__add__(1.0) == 2.0
|
||||
@@ -74,6 +121,8 @@ assert 2.0.__sub__(1.0) == 1.0
|
||||
assert 2.0.__rmul__(1.0) == 2.0
|
||||
assert 1.0.__truediv__(2.0) == 0.5
|
||||
assert 1.0.__rtruediv__(2.0) == 2.0
|
||||
assert 2.5.__divmod__(2.0) == (1.0, 0.5)
|
||||
assert 2.0.__rdivmod__(2.5) == (1.0, 0.5)
|
||||
|
||||
assert 1.0.__add__(1) == 2.0
|
||||
assert 1.0.__radd__(1) == 2.0
|
||||
@@ -83,8 +132,41 @@ assert 1.0.__truediv__(2) == 0.5
|
||||
assert 1.0.__rtruediv__(2) == 2.0
|
||||
assert 2.0.__mul__(1) == 2.0
|
||||
assert 2.0.__rsub__(1) == -1.0
|
||||
assert 2.0.__mod__(2) == 0.0
|
||||
assert 2.0.__rmod__(2) == 0.0
|
||||
assert_raises(ZeroDivisionError, lambda: 2.0 / 0)
|
||||
assert_raises(ZeroDivisionError, lambda: 2.0 // 0)
|
||||
assert_raises(ZeroDivisionError, lambda: 2.0 % 0)
|
||||
assert_raises(ZeroDivisionError, lambda: divmod(2.0, 0))
|
||||
assert_raises(ZeroDivisionError, lambda: 2 / 0.0)
|
||||
assert_raises(ZeroDivisionError, lambda: 2 // 0.0)
|
||||
assert_raises(ZeroDivisionError, lambda: 2 % 0.0)
|
||||
# assert_raises(ZeroDivisionError, lambda: divmod(2, 0.0))
|
||||
|
||||
assert 1.2.__int__() == 1
|
||||
assert 1.2.__float__() == 1.2
|
||||
assert 1.2.__trunc__() == 1
|
||||
assert int(1.2) == 1
|
||||
assert float(1.2) == 1.2
|
||||
assert math.trunc(1.2) == 1
|
||||
assert_raises(OverflowError, float('inf').__trunc__)
|
||||
assert_raises(ValueError, float('nan').__trunc__)
|
||||
assert 0.5.__round__() == 0.0
|
||||
assert 1.5.__round__() == 2.0
|
||||
assert 0.5.__round__(0) == 0.0
|
||||
assert 1.5.__round__(0) == 2.0
|
||||
assert 0.5.__round__(None) == 0.0
|
||||
assert 1.5.__round__(None) == 2.0
|
||||
assert_raises(OverflowError, float('inf').__round__)
|
||||
assert_raises(ValueError, float('nan').__round__)
|
||||
|
||||
assert 1.2 ** 2 == 1.44
|
||||
assert_raises(OverflowError, lambda: 1.2 ** (10 ** 1000))
|
||||
assert 3 ** 2.0 == 9.0
|
||||
|
||||
assert (1.7).real == 1.7
|
||||
assert (1.7).imag == 0.0
|
||||
assert (1.7).conjugate() == 1.7
|
||||
assert (1.3).is_integer() == False
|
||||
assert (1.0).is_integer() == True
|
||||
|
||||
@@ -106,3 +188,29 @@ assert_raises(OverflowError, float('inf').as_integer_ratio)
|
||||
assert_raises(OverflowError, float('-inf').as_integer_ratio)
|
||||
assert_raises(ValueError, float('nan').as_integer_ratio)
|
||||
|
||||
assert str(1.0) == '1.0'
|
||||
assert str(0.0) == '0.0'
|
||||
assert str(1.123456789) == '1.123456789'
|
||||
|
||||
# Test special case for lexer, float starts with a dot:
|
||||
a = .5
|
||||
assert a == 0.5
|
||||
|
||||
assert float.fromhex('0x0.0p+0') == 0.0
|
||||
assert float.fromhex('-0x0.0p+0') == -0.0
|
||||
assert float.fromhex('0x1.000000p+0') == 1.0
|
||||
assert float.fromhex('-0x1.800000p+0') == -1.5
|
||||
assert float.fromhex('inf') == float('inf')
|
||||
assert math.isnan(float.fromhex('nan'))
|
||||
|
||||
assert (0.0).hex() == '0x0.0p+0'
|
||||
assert (-0.0).hex() == '-0x0.0p+0'
|
||||
assert (1.0).hex() == '0x1.0000000000000p+0'
|
||||
assert (-1.5).hex() == '-0x1.8000000000000p+0'
|
||||
assert float('inf').hex() == 'inf'
|
||||
assert float('-inf').hex() == '-inf'
|
||||
assert float('nan').hex() == 'nan'
|
||||
|
||||
#for _ in range(10000):
|
||||
# f = random.random() * random.randint(0, 0x10000000000000000)
|
||||
# assert f == float.fromhex(f.hex())
|
||||
@@ -10,3 +10,15 @@ else:
|
||||
x = 3
|
||||
|
||||
assert x == 3
|
||||
|
||||
y = []
|
||||
for x, in [(9,), [2]]:
|
||||
y.append(x)
|
||||
|
||||
assert y == [9, 2], str(y)
|
||||
|
||||
y = []
|
||||
for x, *z in [(9,88,'b'), [2, 'bla'], [None]*4]:
|
||||
y.append(z)
|
||||
|
||||
assert y == [[88, 'b'], ['bla'], [None]*3], str(y)
|
||||
@@ -0,0 +1,2 @@
|
||||
import __hello__
|
||||
assert __hello__.initialized == True
|
||||
@@ -1,4 +1,74 @@
|
||||
|
||||
__name__ = "function"
|
||||
|
||||
|
||||
def foo():
|
||||
"""test"""
|
||||
return 42
|
||||
|
||||
assert foo() == 42
|
||||
assert foo.__doc__ == "test"
|
||||
assert foo.__name__ == "foo"
|
||||
assert foo.__qualname__ == "foo"
|
||||
assert foo.__module__ == "function"
|
||||
|
||||
def my_func(a,):
|
||||
return a+2
|
||||
|
||||
assert my_func(2) == 4
|
||||
|
||||
def fubar():
|
||||
return 42,
|
||||
|
||||
assert fubar() == (42,)
|
||||
|
||||
def f1():
|
||||
|
||||
"""test1"""
|
||||
pass
|
||||
|
||||
assert f1.__doc__ == "test1"
|
||||
|
||||
def f2():
|
||||
'''test2'''
|
||||
pass
|
||||
|
||||
assert f2.__doc__ == "test2"
|
||||
|
||||
def f3():
|
||||
"""
|
||||
test3
|
||||
"""
|
||||
pass
|
||||
|
||||
assert f3.__doc__ == "\n test3\n "
|
||||
|
||||
def f4():
|
||||
"test4"
|
||||
pass
|
||||
|
||||
assert f4.__doc__ == "test4"
|
||||
|
||||
|
||||
def revdocstr(f):
|
||||
d = f.__doc__
|
||||
d = d + 'w00t'
|
||||
f.__doc__ = d
|
||||
return f
|
||||
|
||||
@revdocstr
|
||||
def f5():
|
||||
"""abc"""
|
||||
|
||||
assert f5.__doc__ == 'abcw00t', f5.__doc__
|
||||
|
||||
|
||||
def f6():
|
||||
def nested():
|
||||
pass
|
||||
|
||||
assert nested.__name__ == "nested"
|
||||
assert nested.__qualname__ == "f6.<locals>.nested"
|
||||
|
||||
|
||||
f6()
|
||||
@@ -1,21 +1,28 @@
|
||||
from testutils import assertRaises
|
||||
|
||||
|
||||
def sum(x, y):
|
||||
return x+y
|
||||
|
||||
# def total(a, b, c, d):
|
||||
# return sum(sum(a,b), sum(c,d))
|
||||
#
|
||||
# assert total(1,1,1,1) == 4
|
||||
# assert total(1,2,3,4) == 10
|
||||
def total(a, b, c, d):
|
||||
return sum(sum(a,b), sum(c,d))
|
||||
|
||||
|
||||
assert total(1,1,1,1) == 4
|
||||
assert total(1,2,3,4) == 10
|
||||
|
||||
assert sum(1, 1) == 2
|
||||
assert sum(1, 3) == 4
|
||||
|
||||
|
||||
def sum2y(x, y):
|
||||
return x+y*2
|
||||
|
||||
|
||||
assert sum2y(1, 1) == 3
|
||||
assert sum2y(1, 3) == 7
|
||||
|
||||
|
||||
def va(a, b=2, *c, d, **e):
|
||||
assert a == 1
|
||||
assert b == 22
|
||||
@@ -23,22 +30,68 @@ def va(a, b=2, *c, d, **e):
|
||||
assert d == 1337
|
||||
assert e['f'] == 42
|
||||
|
||||
|
||||
va(1, 22, 3, 4, d=1337, f=42)
|
||||
|
||||
assert va.__defaults__ == (2,)
|
||||
assert va.__kwdefaults__ is None
|
||||
|
||||
|
||||
def va2(*args, **kwargs):
|
||||
assert args == (5, 4)
|
||||
assert len(kwargs) == 0
|
||||
|
||||
|
||||
va2(5, 4)
|
||||
x = (5, 4)
|
||||
va2(*x)
|
||||
|
||||
va2(5, *x[1:])
|
||||
# def va3(x, *, b=2):
|
||||
# pass
|
||||
|
||||
# va3(1, 2, 3, b=10)
|
||||
|
||||
def va3(x, *, a, b=2, c=9):
|
||||
return x + b + c
|
||||
|
||||
|
||||
assert va3(1, a=1, b=10) == 20
|
||||
|
||||
with assertRaises(TypeError):
|
||||
va3(1, 2, 3, a=1, b=10)
|
||||
|
||||
with assertRaises(TypeError):
|
||||
va3(1, b=10)
|
||||
|
||||
|
||||
assert va3.__defaults__ is None
|
||||
kw_defaults = va3.__kwdefaults__
|
||||
# assert va3.__kwdefaults__ == {'b': 2, 'c': 9}
|
||||
assert set(kw_defaults) == {'b', 'c'}
|
||||
assert kw_defaults['b'] == 2
|
||||
assert kw_defaults['c'] == 9
|
||||
|
||||
x = {'f': 42, 'e': 1337}
|
||||
y = {'d': 1337}
|
||||
va(1, 22, 3, 4, **x, **y)
|
||||
|
||||
# star arg after keyword args:
|
||||
def fubar(x, y, obj=None):
|
||||
assert x == 4
|
||||
assert y == 5
|
||||
assert obj == 6
|
||||
|
||||
rest = [4, 5]
|
||||
fubar(obj=6, *rest)
|
||||
|
||||
|
||||
# https://www.python.org/dev/peps/pep-0468/
|
||||
def func(**kwargs):
|
||||
return list(kwargs.items())
|
||||
|
||||
empty_kwargs = func()
|
||||
assert empty_kwargs == []
|
||||
|
||||
kwargs = func(a=1, b=2)
|
||||
assert kwargs == [('a', 1), ('b', 2)]
|
||||
|
||||
kwargs = func(a=1, b=2, c=3)
|
||||
assert kwargs == [('a', 1), ('b', 2), ('c', 3)]
|
||||
@@ -1,3 +1,5 @@
|
||||
from testutils import assertRaises
|
||||
|
||||
|
||||
r = []
|
||||
|
||||
@@ -35,3 +37,30 @@ r = list(g3())
|
||||
# print(r)
|
||||
assert r == [23, 1, 2, 3, 44]
|
||||
|
||||
def g4():
|
||||
yield
|
||||
yield 2,
|
||||
|
||||
r = list(g4())
|
||||
assert r == [None, (2,)]
|
||||
|
||||
|
||||
def catch_exception():
|
||||
try:
|
||||
yield 1
|
||||
except ValueError:
|
||||
yield 2
|
||||
yield 3
|
||||
|
||||
|
||||
g = catch_exception()
|
||||
assert next(g) == 1
|
||||
|
||||
assert g.throw(ValueError, ValueError(), None) == 2
|
||||
assert next(g) == 3
|
||||
|
||||
g = catch_exception()
|
||||
assert next(g) == 1
|
||||
|
||||
with assertRaises(KeyError):
|
||||
assert g.throw(KeyError, KeyError(), None) == 2
|
||||
@@ -0,0 +1,60 @@
|
||||
from testutils import assertRaises
|
||||
|
||||
# Test global and nonlocal funkyness
|
||||
|
||||
a = 2
|
||||
|
||||
def b():
|
||||
global a
|
||||
a = 4
|
||||
|
||||
assert a == 2
|
||||
b()
|
||||
assert a == 4
|
||||
|
||||
|
||||
def x():
|
||||
def y():
|
||||
nonlocal b
|
||||
b = 3
|
||||
b = 2
|
||||
y()
|
||||
return b
|
||||
|
||||
res = x()
|
||||
assert res == 3, str(res)
|
||||
|
||||
# Invalid syntax:
|
||||
src = """
|
||||
b = 2
|
||||
global b
|
||||
"""
|
||||
|
||||
with assertRaises(SyntaxError):
|
||||
exec(src)
|
||||
|
||||
# Invalid syntax:
|
||||
src = """
|
||||
nonlocal c
|
||||
"""
|
||||
|
||||
with assertRaises(SyntaxError):
|
||||
exec(src)
|
||||
|
||||
|
||||
# Invalid syntax:
|
||||
src = """
|
||||
def f():
|
||||
def x():
|
||||
nonlocal c
|
||||
c = 2
|
||||
"""
|
||||
|
||||
with assertRaises(SyntaxError):
|
||||
exec(src)
|
||||
|
||||
|
||||
# class X:
|
||||
# nonlocal c
|
||||
# c = 2
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
from testutils import assertRaises
|
||||
|
||||
|
||||
class A:
|
||||
pass
|
||||
|
||||
|
||||
assert type(hash(None)) is int
|
||||
assert type(hash(object())) is int
|
||||
assert type(hash(A())) is int
|
||||
assert type(hash(1)) is int
|
||||
assert type(hash(1.1)) is int
|
||||
assert type(hash("")) is int
|
||||
|
||||
with assertRaises(TypeError):
|
||||
hash({})
|
||||
|
||||
with assertRaises(TypeError):
|
||||
hash(set())
|
||||
|
||||
with assertRaises(TypeError):
|
||||
hash([])
|
||||
@@ -0,0 +1,28 @@
|
||||
import _imp
|
||||
import time as import_time
|
||||
|
||||
assert _imp.is_builtin("time") == True
|
||||
assert _imp.is_builtin("os") == False
|
||||
assert _imp.is_builtin("not existing module") == False
|
||||
|
||||
assert _imp.is_frozen("__hello__") == True
|
||||
assert _imp.is_frozen("os") == False
|
||||
|
||||
class FakeSpec:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
A = FakeSpec("time")
|
||||
|
||||
imp_time = _imp.create_builtin(A)
|
||||
assert imp_time.sleep == import_time.sleep
|
||||
|
||||
B = FakeSpec("not existing module")
|
||||
assert _imp.create_builtin(B) == None
|
||||
|
||||
_imp.exec_builtin(imp_time) == 0
|
||||
|
||||
_imp.get_frozen_object("__hello__")
|
||||
|
||||
hello = _imp.init_frozen("__hello__")
|
||||
assert hello.initialized == True
|
||||
@@ -3,6 +3,7 @@ from import_target import func, other_func
|
||||
from import_target import func as aliased_func, other_func as aliased_other_func
|
||||
from import_star import *
|
||||
|
||||
import import_mutual1
|
||||
assert import_target.X == import_target.func()
|
||||
assert import_target.X == func()
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import os
|
||||
|
||||
def import_file():
|
||||
assert os.path.basename(__file__) == "import_file.py"
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
# Mutual recursive import:
|
||||
import import_mutual2
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
# Mutual recursive import:
|
||||
import import_mutual1
|
||||
+72
-1
@@ -1,4 +1,4 @@
|
||||
from testutils import assert_raises
|
||||
from testutils import assert_raises, assertRaises
|
||||
|
||||
# int to int comparisons
|
||||
|
||||
@@ -37,6 +37,9 @@ assert (2).__mul__(1) == 2
|
||||
assert (2).__rmul__(1) == 2
|
||||
assert (2).__truediv__(1) == 2.0
|
||||
assert (2).__rtruediv__(1) == 0.5
|
||||
assert (2).__pow__(3) == 8
|
||||
assert (10).__pow__(-1) == 0.1
|
||||
assert (2).__rpow__(3) == 9
|
||||
|
||||
# real/imag attributes
|
||||
assert (1).real == 1
|
||||
@@ -58,3 +61,71 @@ assert (2).__mul__(1.0) == NotImplemented
|
||||
assert (2).__rmul__(1.0) == NotImplemented
|
||||
assert (2).__truediv__(1.0) == NotImplemented
|
||||
assert (2).__rtruediv__(1.0) == NotImplemented
|
||||
assert (2).__pow__(3.0) == NotImplemented
|
||||
assert (2).__rpow__(3.0) == NotImplemented
|
||||
|
||||
assert 10 // 4 == 2
|
||||
assert -10 // 4 == -3
|
||||
assert 10 // -4 == -3
|
||||
assert -10 // -4 == 2
|
||||
|
||||
assert int() == 0
|
||||
assert int("101", 2) == 5
|
||||
assert int("101", base=2) == 5
|
||||
assert int(1) == 1
|
||||
|
||||
assert int.from_bytes(b'\x00\x10', 'big') == 16
|
||||
assert int.from_bytes(b'\x00\x10', 'little') == 4096
|
||||
assert int.from_bytes(b'\xfc\x00', 'big', signed=True) == -1024
|
||||
assert int.from_bytes(b'\xfc\x00', 'big', signed=False) == 64512
|
||||
|
||||
assert (1024).to_bytes(4, 'big') == b'\x00\x00\x04\x00'
|
||||
assert (1024).to_bytes(2, 'little', signed=True) == b'\x00\x04'
|
||||
assert (-1024).to_bytes(4, 'big', signed=True) == b'\xff\xff\xfc\x00'
|
||||
assert (-1024).to_bytes(4, 'little', signed=True) == b'\x00\xfc\xff\xff'
|
||||
assert (2147483647).to_bytes(8, 'big', signed=False) == b'\x00\x00\x00\x00\x7f\xff\xff\xff'
|
||||
assert (-2147483648).to_bytes(8, 'little', signed=True) == b'\x00\x00\x00\x80\xff\xff\xff\xff'
|
||||
|
||||
with assertRaises(TypeError):
|
||||
int(base=2)
|
||||
|
||||
with assertRaises(TypeError):
|
||||
int(1, base=2)
|
||||
|
||||
with assertRaises(TypeError):
|
||||
# check that first parameter is truly positional only
|
||||
int(val_options=1)
|
||||
|
||||
class A(object):
|
||||
def __int__(self):
|
||||
return 10
|
||||
|
||||
assert int(A()) == 10
|
||||
|
||||
class B(object):
|
||||
pass
|
||||
|
||||
b = B()
|
||||
b.__int__ = lambda: 20
|
||||
|
||||
with assertRaises(TypeError):
|
||||
assert int(b) == 20
|
||||
|
||||
class C(object):
|
||||
def __int__(self):
|
||||
return 'str'
|
||||
|
||||
with assertRaises(TypeError):
|
||||
int(C())
|
||||
|
||||
class I(int):
|
||||
def __int__(self):
|
||||
return 3
|
||||
|
||||
assert int(I(1)) == 3
|
||||
|
||||
class F(float):
|
||||
def __int__(self):
|
||||
return 3
|
||||
|
||||
assert int(F(1.2)) == 3
|
||||
@@ -0,0 +1,18 @@
|
||||
from testutils import assertRaises
|
||||
|
||||
src = """
|
||||
def valid_func():
|
||||
pass
|
||||
|
||||
yield 2
|
||||
"""
|
||||
|
||||
try:
|
||||
compile(src, 'test.py', 'exec')
|
||||
except SyntaxError as ex:
|
||||
assert ex.lineno == 5
|
||||
else:
|
||||
raise AssertionError("Must throw syntax error")
|
||||
|
||||
with assertRaises(SyntaxError):
|
||||
compile('0xX', 'test.py', 'exec')
|
||||
@@ -0,0 +1,32 @@
|
||||
from testutils import assert_raises
|
||||
|
||||
def test_container(x):
|
||||
assert 3 in x
|
||||
assert 4 not in x
|
||||
assert list(x) == list(iter(x))
|
||||
assert list(x) == [0, 1, 2, 3]
|
||||
assert [*x] == [0, 1, 2, 3]
|
||||
lst = []
|
||||
lst.extend(x)
|
||||
assert lst == [0, 1, 2, 3]
|
||||
|
||||
class C:
|
||||
def __iter__(self):
|
||||
return iter([0, 1, 2, 3])
|
||||
test_container(C())
|
||||
|
||||
class C:
|
||||
def __getitem__(self, x):
|
||||
return (0, 1, 2, 3)[x] # raises IndexError on x==4
|
||||
test_container(C())
|
||||
|
||||
class C:
|
||||
def __getitem__(self, x):
|
||||
if x > 3:
|
||||
raise StopIteration
|
||||
return x
|
||||
test_container(C())
|
||||
|
||||
class C: pass
|
||||
assert_raises(TypeError, lambda: 5 in C())
|
||||
assert_raises(TypeError, lambda: iter(C))
|
||||
@@ -25,8 +25,7 @@ assert '[[1]]' == json.dumps(((1,),))
|
||||
assert [1, "string", 1.0, True] == json.loads(json.dumps((1, "string", 1.0, True)))
|
||||
|
||||
assert '{}' == json.dumps({})
|
||||
# TODO: uncomment once dict comparison is implemented
|
||||
# round_trip_test({'a': 'b'})
|
||||
round_trip_test({'a': 'b'})
|
||||
|
||||
assert 1 == json.loads("1")
|
||||
assert -1 == json.loads("-1")
|
||||
|
||||
@@ -14,6 +14,10 @@ assert y == [2, 1, 2, 3, 1, 2, 3]
|
||||
assert x * 0 == [], "list __mul__ by 0 failed"
|
||||
assert x * -1 == [], "list __mul__ by -1 failed"
|
||||
assert x * 2 == [1, 2, 3, 1, 2, 3], "list __mul__ by 2 failed"
|
||||
y = x
|
||||
x *= 2
|
||||
assert y is x
|
||||
assert x == [1, 2, 3] * 2
|
||||
|
||||
# index()
|
||||
assert ['a', 'b', 'c'].index('b') == 1
|
||||
@@ -170,3 +174,317 @@ def f(x):
|
||||
return x
|
||||
assert_raises(ValueError, lambda: lst.sort(key=f)) # "list modified during sort"
|
||||
assert lst == [1, 2, 3, 4, 5]
|
||||
|
||||
# __delitem__
|
||||
x = ['a', 'b', 'c']
|
||||
del x[0]
|
||||
assert x == ['b', 'c']
|
||||
|
||||
x = ['a', 'b', 'c']
|
||||
del x[-1]
|
||||
assert x == ['a', 'b']
|
||||
|
||||
x = y = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15]
|
||||
del x[2:14:3]
|
||||
assert x == y
|
||||
assert x == [1, 2, 4, 5, 7, 8, 11, 12, 14, 15]
|
||||
assert y == [1, 2, 4, 5, 7, 8, 11, 12, 14, 15]
|
||||
|
||||
x = [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15]
|
||||
del x[-5:]
|
||||
assert x == [1, 2, 3, 4, 5, 6, 7, 8, 10]
|
||||
|
||||
x = list(range(12))
|
||||
del x[10:2:-2]
|
||||
assert x == [0,1,2,3,5,7,9,11]
|
||||
|
||||
def bad_del_1():
|
||||
del ['a', 'b']['a']
|
||||
assert_raises(TypeError, bad_del_1)
|
||||
|
||||
def bad_del_2():
|
||||
del ['a', 'b'][2]
|
||||
assert_raises(IndexError, bad_del_2)
|
||||
|
||||
# __setitem__
|
||||
|
||||
# simple index
|
||||
x = [1, 2, 3, 4, 5]
|
||||
x[0] = 'a'
|
||||
assert x == ['a', 2, 3, 4, 5]
|
||||
x[-1] = 'b'
|
||||
assert x == ['a', 2, 3, 4, 'b']
|
||||
# make sure refrences are assigned correctly
|
||||
y = []
|
||||
x[1] = y
|
||||
y.append(100)
|
||||
assert x[1] == y
|
||||
assert x[1] == [100]
|
||||
|
||||
#index bounds
|
||||
def set_index_out_of_bounds_high():
|
||||
x = [0, 1, 2, 3, 4]
|
||||
x[5] = 'a'
|
||||
|
||||
def set_index_out_of_bounds_low():
|
||||
x = [0, 1, 2, 3, 4]
|
||||
x[-6] = 'a'
|
||||
|
||||
assert_raises(IndexError, set_index_out_of_bounds_high)
|
||||
assert_raises(IndexError, set_index_out_of_bounds_low)
|
||||
|
||||
# non stepped slice index
|
||||
a = list(range(10))
|
||||
x = a[:]
|
||||
y = a[:]
|
||||
assert x == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
# replace whole list
|
||||
x[:] = ['a', 'b', 'c']
|
||||
y[::1] = ['a', 'b', 'c']
|
||||
assert x == ['a', 'b', 'c']
|
||||
assert x == y
|
||||
# splice list start
|
||||
x = a[:]
|
||||
y = a[:]
|
||||
z = a[:]
|
||||
zz = a[:]
|
||||
x[:1] = ['a', 'b', 'c']
|
||||
y[0:1] = ['a', 'b', 'c']
|
||||
z[:1:1] = ['a', 'b', 'c']
|
||||
zz[0:1:1] = ['a', 'b', 'c']
|
||||
assert x == ['a', 'b', 'c', 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
assert x == y
|
||||
assert x == z
|
||||
assert x == zz
|
||||
# splice list end
|
||||
x = a[:]
|
||||
y = a[:]
|
||||
z = a[:]
|
||||
zz = a[:]
|
||||
x[5:] = ['a', 'b', 'c']
|
||||
y[5::1] = ['a', 'b', 'c']
|
||||
z[5:10] = ['a', 'b', 'c']
|
||||
zz[5:10:1] = ['a', 'b', 'c']
|
||||
assert x == [0, 1, 2, 3, 4, 'a', 'b', 'c']
|
||||
assert x == y
|
||||
assert x == z
|
||||
assert x == zz
|
||||
# insert sec
|
||||
x = a[:]
|
||||
y = a[:]
|
||||
z = a[:]
|
||||
zz = a[:]
|
||||
x[1:1] = ['a', 'b', 'c']
|
||||
y[1:0] = ['a', 'b', 'c']
|
||||
z[1:1:1] = ['a', 'b', 'c']
|
||||
zz[1:0:1] = ['a', 'b', 'c']
|
||||
assert x == [0, 'a', 'b', 'c', 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
assert x == y
|
||||
assert x == z
|
||||
assert x == zz
|
||||
# same but negative indexes?
|
||||
x = a[:]
|
||||
y = a[:]
|
||||
z = a[:]
|
||||
zz = a[:]
|
||||
x[-1:-1] = ['a', 'b', 'c']
|
||||
y[-1:9] = ['a', 'b', 'c']
|
||||
z[-1:-1:1] = ['a', 'b', 'c']
|
||||
zz[-1:9:1] = ['a', 'b', 'c']
|
||||
assert x == [0, 1, 2, 3, 4, 5, 6, 7, 8, 'a', 'b', 'c', 9]
|
||||
assert x == y
|
||||
assert x == z
|
||||
assert x == zz
|
||||
# splice mid
|
||||
x = a[:]
|
||||
y = a[:]
|
||||
x[3:5] = ['a', 'b', 'c', 'd', 'e']
|
||||
y[3:5:1] = ['a', 'b', 'c', 'd', 'e']
|
||||
assert x == [0, 1, 2, 'a', 'b', 'c', 'd', 'e', 5, 6, 7, 8, 9]
|
||||
assert x == y
|
||||
x = a[:]
|
||||
x[3:5] = ['a']
|
||||
assert x == [0, 1, 2, 'a', 5, 6, 7, 8, 9]
|
||||
# assign empty to non stepped empty slice does nothing
|
||||
x = a[:]
|
||||
y = a[:]
|
||||
x[5:2] = []
|
||||
y[5:2:1] = []
|
||||
assert x == a
|
||||
assert y == a
|
||||
# assign empty to non stepped slice removes elems
|
||||
x = a[:]
|
||||
y = a[:]
|
||||
x[2:8] = []
|
||||
y[2:8:1] = []
|
||||
assert x == [0, 1, 8, 9]
|
||||
assert x == y
|
||||
# make sure refrences are assigned correctly
|
||||
yy = []
|
||||
x = a[:]
|
||||
y = a[:]
|
||||
x[3:5] = ['a', 'b', 'c', 'd', yy]
|
||||
y[3:5:1] = ['a', 'b', 'c', 'd', yy]
|
||||
assert x == [0, 1, 2, 'a', 'b', 'c', 'd', [], 5, 6, 7, 8, 9]
|
||||
assert x == y
|
||||
yy.append(100)
|
||||
assert x == [0, 1, 2, 'a', 'b', 'c', 'd', [100], 5, 6, 7, 8, 9]
|
||||
assert x == y
|
||||
assert x[7] == yy
|
||||
assert x[7] == [100]
|
||||
assert y[7] == yy
|
||||
assert y[7] == [100]
|
||||
|
||||
# no zero step
|
||||
def no_zero_step_set():
|
||||
x = [1, 2, 3, 4, 5]
|
||||
x[0:4:0] = [11, 12, 13, 14, 15]
|
||||
assert_raises(ValueError, no_zero_step_set)
|
||||
|
||||
# stepped slice index
|
||||
# forward slice
|
||||
x = a[:]
|
||||
x[2:8:2] = ['a', 'b', 'c']
|
||||
assert x == [0, 1, 'a', 3, 'b', 5, 'c', 7, 8, 9]
|
||||
x = a[:]
|
||||
y = a[:]
|
||||
z = a[:]
|
||||
zz = a[:]
|
||||
c = ['a', 'b', 'c', 'd', 'e']
|
||||
x[::2] = c
|
||||
y[-10::2] = c
|
||||
z[0:10:2] = c
|
||||
zz[-13:13:2] = c # slice indexes will be truncated to bounds
|
||||
assert x == ['a', 1, 'b', 3, 'c', 5, 'd', 7, 'e', 9]
|
||||
assert x == y
|
||||
assert x == z
|
||||
assert x == zz
|
||||
# backward slice
|
||||
x = a[:]
|
||||
x[8:2:-2] = ['a', 'b', 'c']
|
||||
assert x == [0, 1, 2, 3, 'c', 5, 'b', 7, 'a', 9]
|
||||
x = a[:]
|
||||
y = a[:]
|
||||
z = a[:]
|
||||
zz = a[:]
|
||||
c = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
|
||||
x[::-1] = c
|
||||
y[9:-11:-1] = c
|
||||
z[9::-1] = c
|
||||
zz[11:-13:-1] = c # slice indexes will be truncated to bounds
|
||||
assert x == ['j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a']
|
||||
assert x == y
|
||||
assert x == z
|
||||
assert x == zz
|
||||
# step size bigger than len
|
||||
x = a[:]
|
||||
x[::200] = ['a']
|
||||
assert x == ['a', 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
x = a[:]
|
||||
x[5::200] = ['a']
|
||||
assert x == [0, 1, 2, 3, 4, 'a', 6, 7, 8, 9]
|
||||
|
||||
# bad stepped slices
|
||||
def stepped_slice_assign_too_big():
|
||||
x = [0, 1, 2, 3, 4]
|
||||
x[::2] = ['a', 'b', 'c', 'd']
|
||||
|
||||
assert_raises(ValueError, stepped_slice_assign_too_big)
|
||||
|
||||
def stepped_slice_assign_too_small():
|
||||
x = [0, 1, 2, 3, 4]
|
||||
x[::2] = ['a', 'b']
|
||||
|
||||
assert_raises(ValueError, stepped_slice_assign_too_small)
|
||||
|
||||
# must assign iter t0 slice
|
||||
def must_assign_iter_to_slice():
|
||||
x = [0, 1, 2, 3, 4]
|
||||
x[::2] = 42
|
||||
|
||||
assert_raises(TypeError, must_assign_iter_to_slice)
|
||||
|
||||
# other iterables?
|
||||
a = list(range(10))
|
||||
|
||||
# string
|
||||
x = a[:]
|
||||
x[3:8] = "abcdefghi"
|
||||
assert x == [0, 1, 2, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 8, 9]
|
||||
|
||||
# tuple
|
||||
x = a[:]
|
||||
x[3:8] = (11, 12, 13, 14, 15)
|
||||
assert x == [0, 1, 2, 11, 12, 13, 14, 15, 8, 9]
|
||||
|
||||
# class
|
||||
# __next__
|
||||
class CIterNext:
|
||||
def __init__(self, sec=(1, 2, 3)):
|
||||
self.sec = sec
|
||||
self.index = 0
|
||||
def __iter__(self):
|
||||
return self
|
||||
def __next__(self):
|
||||
if self.index >= len(self.sec):
|
||||
raise StopIteration
|
||||
v = self.sec[self.index]
|
||||
self.index += 1
|
||||
return v
|
||||
|
||||
x = list(range(10))
|
||||
x[3:8] = CIterNext()
|
||||
assert x == [0, 1, 2, 1, 2, 3, 8, 9]
|
||||
|
||||
# __iter__ yield
|
||||
class CIter:
|
||||
def __init__(self, sec=(1, 2, 3)):
|
||||
self.sec = sec
|
||||
def __iter__(self):
|
||||
for n in self.sec:
|
||||
yield n
|
||||
|
||||
x = list(range(10))
|
||||
x[3:8] = CIter()
|
||||
assert x == [0, 1, 2, 1, 2, 3, 8, 9]
|
||||
|
||||
# __getitem but no __iter__ sequence
|
||||
class CGetItem:
|
||||
def __init__(self, sec=(1, 2, 3)):
|
||||
self.sec = sec
|
||||
def __getitem__(self, sub):
|
||||
return self.sec[sub]
|
||||
|
||||
x = list(range(10))
|
||||
x[3:8] = CGetItem()
|
||||
assert x == [0, 1, 2, 1, 2, 3, 8, 9]
|
||||
|
||||
# iter raises error
|
||||
class CIterError:
|
||||
def __iter__(self):
|
||||
for i in range(10):
|
||||
if i > 5:
|
||||
raise RuntimeError
|
||||
yield i
|
||||
|
||||
def bad_iter_assign():
|
||||
x = list(range(10))
|
||||
x[3:8] = CIterError()
|
||||
|
||||
assert_raises(RuntimeError, bad_iter_assign)
|
||||
|
||||
# slice assign when step or stop is -1
|
||||
a = list(range(10))
|
||||
x = a[:]
|
||||
x[-1:-5:-1] = ['a', 'b', 'c', 'd']
|
||||
assert x == [0, 1, 2, 3, 4, 5, 'd', 'c', 'b', 'a']
|
||||
x = a[:]
|
||||
x[-5:-1:-1] = []
|
||||
assert x == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
|
||||
# is step != 1 and start or stop of slice == -1
|
||||
x = list(range(10))
|
||||
del x[-1:-5:-1]
|
||||
assert x == [0, 1, 2, 3, 4, 5]
|
||||
x = list(range(10))
|
||||
del x[-5:-1:-1]
|
||||
@@ -0,0 +1,8 @@
|
||||
# Integer literals
|
||||
assert 0b101010 == 42
|
||||
assert 0B101010 == 42
|
||||
assert 0o777 == 511
|
||||
assert 0O777 == 511
|
||||
assert 0xcafebabe == 3405691582
|
||||
assert 0Xcafebabe == 3405691582
|
||||
assert 0xCAFEBABE == 3405691582
|
||||
@@ -0,0 +1,18 @@
|
||||
from testutils import assertRaises
|
||||
|
||||
class A(dict):
|
||||
def a():
|
||||
pass
|
||||
|
||||
def b():
|
||||
pass
|
||||
|
||||
|
||||
assert A.__dict__['a'] == A.a
|
||||
with assertRaises(KeyError):
|
||||
A.__dict__['not_here']
|
||||
|
||||
assert 'b' in A.__dict__
|
||||
assert 'c' not in A.__dict__
|
||||
|
||||
assert '__dict__' in A.__dict__
|
||||
@@ -1,3 +1,5 @@
|
||||
from testutils import assert_raises
|
||||
|
||||
a = 4
|
||||
|
||||
#print(a ** 3)
|
||||
@@ -12,18 +14,58 @@ assert a ** 3 == 64
|
||||
assert a * 3 == 12
|
||||
assert a / 2 == 2
|
||||
assert 2 == a / 2
|
||||
# assert a % 3 == 1
|
||||
assert a % 3 == 1
|
||||
assert a - 3 == 1
|
||||
assert -a == -4
|
||||
assert +a == 4
|
||||
|
||||
# import math
|
||||
# assert(math.exp(2) == math.exp(2.0))
|
||||
# assert(math.exp(True) == math.exp(1.0))
|
||||
#
|
||||
# class Conversible():
|
||||
# def __float__(self):
|
||||
# print("Converting to float now!")
|
||||
# return 1.1111
|
||||
#
|
||||
# assert math.log(1.1111) == math.log(Conversible())
|
||||
assert round(1.2) == 1
|
||||
assert round(1.8) == 2
|
||||
assert round(0.5) == 0
|
||||
assert round(1.5) == 2
|
||||
assert round(-0.5) == 0
|
||||
assert round(-1.5) == -2
|
||||
|
||||
assert_raises(
|
||||
ValueError,
|
||||
lambda: round(float('nan')),
|
||||
'ValueError: cannot convert float NaN to integer')
|
||||
assert_raises(
|
||||
OverflowError,
|
||||
lambda: round(float('inf')),
|
||||
'OverflowError: cannot convert float NaN to integer')
|
||||
assert_raises(
|
||||
OverflowError,
|
||||
lambda: round(-float('inf')),
|
||||
'OverflowError: cannot convert float NaN to integer')
|
||||
|
||||
assert pow(0, 0) == 1
|
||||
assert pow(2, 2) == 4
|
||||
assert pow(1, 2.0) == 1.0
|
||||
assert pow(2.0, 1) == 2.0
|
||||
assert pow(0, 10**1000) == 0
|
||||
assert pow(1, 10**1000) == 1
|
||||
assert pow(-1, 10**1000+1) == -1
|
||||
assert pow(-1, 10**1000) == 1
|
||||
|
||||
assert pow(2, 4, 5) == 1
|
||||
assert_raises(
|
||||
TypeError,
|
||||
lambda: pow(2, 4, 5.0),
|
||||
'pow() 3rd argument not allowed unless all arguments are integers')
|
||||
assert_raises(
|
||||
TypeError,
|
||||
lambda: pow(2, 4.0, 5),
|
||||
'pow() 3rd argument not allowed unless all arguments are integers')
|
||||
assert_raises(
|
||||
TypeError,
|
||||
lambda: pow(2.0, 4, 5),
|
||||
'pow() 3rd argument not allowed unless all arguments are integers')
|
||||
assert_raises(
|
||||
ValueError,
|
||||
lambda: pow(2, -1, 5),
|
||||
'pow() 2nd argument cannot be negative when 3rd argument specified')
|
||||
assert_raises(
|
||||
ValueError,
|
||||
lambda: pow(2, 2, 0),
|
||||
'pow() 3rd argument cannot be 0')
|
||||
@@ -0,0 +1,89 @@
|
||||
import math
|
||||
from testutils import assertRaises, assert_raises
|
||||
|
||||
# assert(math.exp(2) == math.exp(2.0))
|
||||
# assert(math.exp(True) == math.exp(1.0))
|
||||
#
|
||||
# class Conversible():
|
||||
# def __float__(self):
|
||||
# print("Converting to float now!")
|
||||
# return 1.1111
|
||||
#
|
||||
# assert math.log(1.1111) == math.log(Conversible())
|
||||
|
||||
# roundings
|
||||
assert int.__trunc__
|
||||
assert int.__floor__
|
||||
assert int.__ceil__
|
||||
|
||||
# assert float.__trunc__
|
||||
with assertRaises(AttributeError):
|
||||
assert float.__floor__
|
||||
with assertRaises(AttributeError):
|
||||
assert float.__ceil__
|
||||
|
||||
assert math.trunc(2) == 2
|
||||
assert math.ceil(3) == 3
|
||||
assert math.floor(4) == 4
|
||||
|
||||
assert math.trunc(2.2) == 2
|
||||
assert math.ceil(3.3) == 4
|
||||
assert math.floor(4.4) == 4
|
||||
|
||||
class A(object):
|
||||
def __trunc__(self):
|
||||
return 2
|
||||
|
||||
def __ceil__(self):
|
||||
return 3
|
||||
|
||||
def __floor__(self):
|
||||
return 4
|
||||
|
||||
assert math.trunc(A()) == 2
|
||||
assert math.ceil(A()) == 3
|
||||
assert math.floor(A()) == 4
|
||||
|
||||
class A(object):
|
||||
def __trunc__(self):
|
||||
return 2.2
|
||||
|
||||
def __ceil__(self):
|
||||
return 3.3
|
||||
|
||||
def __floor__(self):
|
||||
return 4.4
|
||||
|
||||
assert math.trunc(A()) == 2.2
|
||||
assert math.ceil(A()) == 3.3
|
||||
assert math.floor(A()) == 4.4
|
||||
|
||||
class A(object):
|
||||
def __trunc__(self):
|
||||
return 'trunc'
|
||||
|
||||
def __ceil__(self):
|
||||
return 'ceil'
|
||||
|
||||
def __floor__(self):
|
||||
return 'floor'
|
||||
|
||||
assert math.trunc(A()) == 'trunc'
|
||||
assert math.ceil(A()) == 'ceil'
|
||||
assert math.floor(A()) == 'floor'
|
||||
|
||||
with assertRaises(TypeError):
|
||||
math.trunc(object())
|
||||
with assertRaises(TypeError):
|
||||
math.ceil(object())
|
||||
with assertRaises(TypeError):
|
||||
math.floor(object())
|
||||
|
||||
assert str(math.frexp(0.0)) == str((+0.0, 0))
|
||||
assert str(math.frexp(-0.0)) == str((-0.0, 0))
|
||||
assert math.frexp(1) == (0.5, 1)
|
||||
assert math.frexp(1.5) == (0.75, 1)
|
||||
|
||||
assert math.frexp(float('inf')) == (float('inf'), 0)
|
||||
assert str(math.frexp(float('nan'))) == str((float('nan'), 0))
|
||||
assert_raises(TypeError, lambda: math.frexp(None))
|
||||
@@ -12,9 +12,8 @@ assert "foo" in "foobar"
|
||||
assert "whatever" not in "foobar"
|
||||
|
||||
# test bytes
|
||||
# TODO: uncomment this when bytes are implemented
|
||||
# assert b"foo" in b"foobar"
|
||||
# assert b"whatever" not in b"foobar"
|
||||
assert b"foo" in b"foobar"
|
||||
assert b"whatever" not in b"foobar"
|
||||
assert b"1" < b"2"
|
||||
assert b"1" <= b"2"
|
||||
assert b"5" <= b"5"
|
||||
@@ -32,18 +31,20 @@ assert 1 in set([1, 2])
|
||||
assert 3 not in set([1, 2])
|
||||
|
||||
# test dicts
|
||||
# TODO: test dicts when keys other than strings will be allowed
|
||||
assert "a" in {"a": 0, "b": 0}
|
||||
assert "c" not in {"a": 0, "b": 0}
|
||||
assert 1 in {1: 5, 7: 12}
|
||||
assert 5 not in {9: 10, 50: 100}
|
||||
assert True in {True: 5}
|
||||
assert False not in {True: 5}
|
||||
|
||||
# test iter
|
||||
assert 3 in iter([1, 2, 3])
|
||||
assert 3 not in iter([1, 2])
|
||||
|
||||
# test sequence
|
||||
# TODO: uncomment this when ranges are usable
|
||||
# assert 1 in range(0, 2)
|
||||
# assert 3 not in range(0, 2)
|
||||
assert 1 in range(0, 2)
|
||||
assert 3 not in range(0, 2)
|
||||
|
||||
# test __contains__ in user objects
|
||||
class MyNotContainingClass():
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from testutils import assert_raises
|
||||
import io
|
||||
|
||||
print(2 + 3)
|
||||
|
||||
@@ -9,3 +10,7 @@ try:
|
||||
print('test', end=None, sep=None, flush=None)
|
||||
except:
|
||||
assert False, 'Expected None passed to end, sep, and flush to not raise errors'
|
||||
|
||||
buf = io.StringIO()
|
||||
print('hello, world', file=buf)
|
||||
assert buf.getvalue() == 'hello, world\n', buf.getvalue()
|
||||
@@ -59,6 +59,8 @@ with assertRaises(AttributeError):
|
||||
with assertRaises(TypeError):
|
||||
property.__new__(object)
|
||||
|
||||
# assert p.__doc__ is None
|
||||
|
||||
|
||||
p1 = property("a", "b", "c")
|
||||
|
||||
@@ -75,3 +77,7 @@ assert p1.setter(None).fset == "b"
|
||||
assert p1.deleter(None).fdel == "c"
|
||||
|
||||
assert p1.__get__(None, object) is p1
|
||||
# assert p1.__doc__ is 'a'.__doc__
|
||||
|
||||
p2 = property('a', doc='pdoc')
|
||||
# assert p2.__doc__ == 'pdoc'
|
||||
+184
-1
@@ -27,6 +27,26 @@ assert set([1,2]) < set([1,2,3])
|
||||
assert not set([1,2]) < set([1,2])
|
||||
assert not set([1,3]) < set([1,2])
|
||||
|
||||
assert (set() == []) is False
|
||||
assert set().__eq__([]) == NotImplemented
|
||||
assert_raises(TypeError, lambda: set() < [], "'<' not supported between instances of 'set' and 'list'")
|
||||
assert_raises(TypeError, lambda: set() <= [], "'<=' not supported between instances of 'set' and 'list'")
|
||||
assert_raises(TypeError, lambda: set() > [], "'>' not supported between instances of 'set' and 'list'")
|
||||
assert_raises(TypeError, lambda: set() >= [], "'>=' not supported between instances of 'set' and 'list'")
|
||||
assert set().issuperset([])
|
||||
assert set().issubset([])
|
||||
assert not set().issuperset([1, 2, 3])
|
||||
assert set().issubset([1, 2])
|
||||
|
||||
assert (set() == 3) is False
|
||||
assert set().__eq__(3) == NotImplemented
|
||||
assert_raises(TypeError, lambda: set() < 3, "'int' object is not iterable")
|
||||
assert_raises(TypeError, lambda: set() <= 3, "'int' object is not iterable")
|
||||
assert_raises(TypeError, lambda: set() > 3, "'int' object is not iterable")
|
||||
assert_raises(TypeError, lambda: set() >= 3, "'int' object is not iterable")
|
||||
assert_raises(TypeError, lambda: set().issuperset(3), "'int' object is not iterable")
|
||||
assert_raises(TypeError, lambda: set().issubset(3), "'int' object is not iterable")
|
||||
|
||||
class Hashable(object):
|
||||
def __init__(self, obj):
|
||||
self.obj = obj
|
||||
@@ -49,27 +69,45 @@ assert len(a) == 0
|
||||
|
||||
assert set([1,2,3]).union(set([4,5])) == set([1,2,3,4,5])
|
||||
assert set([1,2,3]).union(set([1,2,3,4,5])) == set([1,2,3,4,5])
|
||||
assert set([1,2,3]).union([1,2,3,4,5]) == set([1,2,3,4,5])
|
||||
|
||||
assert set([1,2,3]) | set([4,5]) == set([1,2,3,4,5])
|
||||
assert set([1,2,3]) | set([1,2,3,4,5]) == set([1,2,3,4,5])
|
||||
assert_raises(TypeError, lambda: set([1,2,3]) | [1,2,3,4,5])
|
||||
|
||||
assert set([1,2,3]).intersection(set([1,2])) == set([1,2])
|
||||
assert set([1,2,3]).intersection(set([5,6])) == set([])
|
||||
assert set([1,2,3]).intersection([1,2]) == set([1,2])
|
||||
|
||||
assert set([1,2,3]) & set([4,5]) == set([])
|
||||
assert set([1,2,3]) & set([1,2,3,4,5]) == set([1,2,3])
|
||||
assert_raises(TypeError, lambda: set([1,2,3]) & [1,2,3,4,5])
|
||||
|
||||
assert set([1,2,3]).difference(set([1,2])) == set([3])
|
||||
assert set([1,2,3]).difference(set([5,6])) == set([1,2,3])
|
||||
assert set([1,2,3]).difference([1,2]) == set([3])
|
||||
|
||||
assert set([1,2,3]) - set([4,5]) == set([1,2,3])
|
||||
assert set([1,2,3]) - set([1,2,3,4,5]) == set([])
|
||||
assert_raises(TypeError, lambda: set([1,2,3]) - [1,2,3,4,5])
|
||||
|
||||
assert set([1,2,3]).symmetric_difference(set([1,2])) == set([3])
|
||||
assert set([1,2,3]).symmetric_difference(set([5,6])) == set([1,2,3,5,6])
|
||||
assert set([1,2,3]).symmetric_difference([1,2]) == set([3])
|
||||
|
||||
assert set([1,2,3]) ^ set([4,5]) == set([1,2,3,4,5])
|
||||
assert set([1,2,3]) ^ set([1,2,3,4,5]) == set([4,5])
|
||||
assert_raises(TypeError, lambda: set([1,2,3]) ^ [1,2,3,4,5])
|
||||
|
||||
assert set([1,2,3]).isdisjoint(set([5,6])) == True
|
||||
assert set([1,2,3]).isdisjoint(set([2,5,6])) == False
|
||||
assert set([1,2,3]).isdisjoint([5,6]) == True
|
||||
|
||||
assert_raises(TypeError, lambda: set() & [])
|
||||
assert_raises(TypeError, lambda: set() | [])
|
||||
assert_raises(TypeError, lambda: set() ^ [])
|
||||
assert_raises(TypeError, lambda: set() + [])
|
||||
assert_raises(TypeError, lambda: set() - [])
|
||||
|
||||
assert_raises(TypeError, lambda: set([[]]))
|
||||
assert_raises(TypeError, lambda: set().add([]))
|
||||
@@ -91,7 +129,7 @@ a = set([1,2])
|
||||
b = a.pop()
|
||||
assert b in [1,2]
|
||||
c = a.pop()
|
||||
assert (c in [1,2] and c != b)
|
||||
assert (c in [1,2] and c != b)
|
||||
assert_raises(KeyError, lambda: a.pop())
|
||||
|
||||
a = set([1,2,3])
|
||||
@@ -111,6 +149,8 @@ a |= set([3,4,5])
|
||||
assert a == set([1,2,3,4,5])
|
||||
with assertRaises(TypeError):
|
||||
a |= 1
|
||||
with assertRaises(TypeError):
|
||||
a |= [1,2,3]
|
||||
|
||||
a = set([1,2,3])
|
||||
a.intersection_update([2,3,4,5])
|
||||
@@ -122,6 +162,8 @@ a &= set([2,3,4,5])
|
||||
assert a == set([2,3])
|
||||
with assertRaises(TypeError):
|
||||
a &= 1
|
||||
with assertRaises(TypeError):
|
||||
a &= [1,2,3]
|
||||
|
||||
a = set([1,2,3])
|
||||
a.difference_update([3,4,5])
|
||||
@@ -133,6 +175,8 @@ a -= set([3,4,5])
|
||||
assert a == set([1,2])
|
||||
with assertRaises(TypeError):
|
||||
a -= 1
|
||||
with assertRaises(TypeError):
|
||||
a -= [1,2,3]
|
||||
|
||||
a = set([1,2,3])
|
||||
a.symmetric_difference_update([3,4,5])
|
||||
@@ -144,3 +188,142 @@ a ^= set([3,4,5])
|
||||
assert a == set([1,2,4,5])
|
||||
with assertRaises(TypeError):
|
||||
a ^= 1
|
||||
with assertRaises(TypeError):
|
||||
a ^= [1,2,3]
|
||||
|
||||
# frozen set
|
||||
|
||||
assert frozenset([1,2]) == frozenset([1,2])
|
||||
assert not frozenset([1,2,3]) == frozenset([1,2])
|
||||
|
||||
assert frozenset([1,2,3]) >= frozenset([1,2])
|
||||
assert frozenset([1,2]) >= frozenset([1,2])
|
||||
assert not frozenset([1,3]) >= frozenset([1,2])
|
||||
|
||||
assert frozenset([1,2,3]).issuperset(frozenset([1,2]))
|
||||
assert frozenset([1,2]).issuperset(frozenset([1,2]))
|
||||
assert not frozenset([1,3]).issuperset(frozenset([1,2]))
|
||||
|
||||
assert frozenset([1,2,3]) > frozenset([1,2])
|
||||
assert not frozenset([1,2]) > frozenset([1,2])
|
||||
assert not frozenset([1,3]) > frozenset([1,2])
|
||||
|
||||
assert frozenset([1,2]) <= frozenset([1,2,3])
|
||||
assert frozenset([1,2]) <= frozenset([1,2])
|
||||
assert not frozenset([1,3]) <= frozenset([1,2])
|
||||
|
||||
assert frozenset([1,2]).issubset(frozenset([1,2,3]))
|
||||
assert frozenset([1,2]).issubset(frozenset([1,2]))
|
||||
assert not frozenset([1,3]).issubset(frozenset([1,2]))
|
||||
|
||||
assert frozenset([1,2]) < frozenset([1,2,3])
|
||||
assert not frozenset([1,2]) < frozenset([1,2])
|
||||
assert not frozenset([1,3]) < frozenset([1,2])
|
||||
|
||||
a = frozenset([1, 2, 3])
|
||||
assert len(a) == 3
|
||||
b = a.copy()
|
||||
assert b == a
|
||||
|
||||
assert frozenset([1,2,3]).union(frozenset([4,5])) == frozenset([1,2,3,4,5])
|
||||
assert frozenset([1,2,3]).union(frozenset([1,2,3,4,5])) == frozenset([1,2,3,4,5])
|
||||
assert frozenset([1,2,3]).union([1,2,3,4,5]) == frozenset([1,2,3,4,5])
|
||||
|
||||
assert frozenset([1,2,3]) | frozenset([4,5]) == frozenset([1,2,3,4,5])
|
||||
assert frozenset([1,2,3]) | frozenset([1,2,3,4,5]) == frozenset([1,2,3,4,5])
|
||||
assert_raises(TypeError, lambda: frozenset([1,2,3]) | [1,2,3,4,5])
|
||||
|
||||
assert frozenset([1,2,3]).intersection(frozenset([1,2])) == frozenset([1,2])
|
||||
assert frozenset([1,2,3]).intersection(frozenset([5,6])) == frozenset([])
|
||||
assert frozenset([1,2,3]).intersection([1,2]) == frozenset([1,2])
|
||||
|
||||
assert frozenset([1,2,3]) & frozenset([4,5]) == frozenset([])
|
||||
assert frozenset([1,2,3]) & frozenset([1,2,3,4,5]) == frozenset([1,2,3])
|
||||
assert_raises(TypeError, lambda: frozenset([1,2,3]) & [1,2,3,4,5])
|
||||
|
||||
assert frozenset([1,2,3]).difference(frozenset([1,2])) == frozenset([3])
|
||||
assert frozenset([1,2,3]).difference(frozenset([5,6])) == frozenset([1,2,3])
|
||||
assert frozenset([1,2,3]).difference([1,2]) == frozenset([3])
|
||||
|
||||
assert frozenset([1,2,3]) - frozenset([4,5]) == frozenset([1,2,3])
|
||||
assert frozenset([1,2,3]) - frozenset([1,2,3,4,5]) == frozenset([])
|
||||
assert_raises(TypeError, lambda: frozenset([1,2,3]) - [1,2,3,4,5])
|
||||
|
||||
assert frozenset([1,2,3]).symmetric_difference(frozenset([1,2])) == frozenset([3])
|
||||
assert frozenset([1,2,3]).symmetric_difference(frozenset([5,6])) == frozenset([1,2,3,5,6])
|
||||
assert frozenset([1,2,3]).symmetric_difference([1,2]) == frozenset([3])
|
||||
|
||||
assert frozenset([1,2,3]) ^ frozenset([4,5]) == frozenset([1,2,3,4,5])
|
||||
assert frozenset([1,2,3]) ^ frozenset([1,2,3,4,5]) == frozenset([4,5])
|
||||
assert_raises(TypeError, lambda: frozenset([1,2,3]) ^ [1,2,3,4,5])
|
||||
|
||||
assert frozenset([1,2,3]).isdisjoint(frozenset([5,6])) == True
|
||||
assert frozenset([1,2,3]).isdisjoint(frozenset([2,5,6])) == False
|
||||
assert frozenset([1,2,3]).isdisjoint([5,6]) == True
|
||||
|
||||
assert_raises(TypeError, lambda: frozenset([[]]))
|
||||
|
||||
a = frozenset([1,2,3])
|
||||
b = set()
|
||||
for e in a:
|
||||
assert e == 1 or e == 2 or e == 3
|
||||
b.add(e)
|
||||
assert a == b
|
||||
|
||||
# set and frozen set
|
||||
assert frozenset([1,2,3]).union(set([4,5])) == frozenset([1,2,3,4,5])
|
||||
assert set([1,2,3]).union(frozenset([4,5])) == set([1,2,3,4,5])
|
||||
|
||||
assert frozenset([1,2,3]) | set([4,5]) == frozenset([1,2,3,4,5])
|
||||
assert set([1,2,3]) | frozenset([4,5]) == set([1,2,3,4,5])
|
||||
|
||||
assert frozenset([1,2,3]).intersection(set([5,6])) == frozenset([])
|
||||
assert set([1,2,3]).intersection(frozenset([5,6])) == set([])
|
||||
|
||||
assert frozenset([1,2,3]) & set([1,2,3,4,5]) == frozenset([1,2,3])
|
||||
assert set([1,2,3]) & frozenset([1,2,3,4,5]) == set([1,2,3])
|
||||
|
||||
assert frozenset([1,2,3]).difference(set([5,6])) == frozenset([1,2,3])
|
||||
assert set([1,2,3]).difference(frozenset([5,6])) == set([1,2,3])
|
||||
|
||||
assert frozenset([1,2,3]) - set([4,5]) == frozenset([1,2,3])
|
||||
assert set([1,2,3]) - frozenset([4,5]) == frozenset([1,2,3])
|
||||
|
||||
assert frozenset([1,2,3]).symmetric_difference(set([1,2])) == frozenset([3])
|
||||
assert set([1,2,3]).symmetric_difference(frozenset([1,2])) == set([3])
|
||||
|
||||
assert frozenset([1,2,3]) ^ set([4,5]) == frozenset([1,2,3,4,5])
|
||||
assert set([1,2,3]) ^ frozenset([4,5]) == set([1,2,3,4,5])
|
||||
|
||||
class A:
|
||||
def __hash__(self):
|
||||
return 1
|
||||
class B:
|
||||
def __hash__(self):
|
||||
return 1
|
||||
|
||||
s = {1, A(), B()}
|
||||
assert len(s) == 3
|
||||
|
||||
s = {True}
|
||||
s.add(1.0)
|
||||
assert str(s) == '{True}'
|
||||
|
||||
class EqObject:
|
||||
def __init__(self, eq):
|
||||
self.eq = eq
|
||||
def __eq__(self, other):
|
||||
return self.eq
|
||||
def __hash__(self):
|
||||
return bool(self.eq)
|
||||
|
||||
assert 'x' == (EqObject('x') == EqObject('x'))
|
||||
s = {EqObject('x')}
|
||||
assert EqObject('x') in s
|
||||
assert '[]' == (EqObject('[]') == EqObject('[]'))
|
||||
s = {EqObject([])}
|
||||
assert EqObject([]) not in s
|
||||
x = object()
|
||||
assert x == (EqObject(x) == EqObject(x))
|
||||
s = {EqObject(x)}
|
||||
assert EqObject(x) in s
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
# Test several statement types
|
||||
|
||||
# small ones, seperated by ';':
|
||||
|
||||
if True:
|
||||
5;4;
|
||||
b=4
|
||||
b;
|
||||
@@ -0,0 +1,39 @@
|
||||
import binascii
|
||||
from testutils import assertRaises
|
||||
|
||||
|
||||
# hexlify tests
|
||||
h = binascii.hexlify
|
||||
|
||||
assert h(b"abc") == b"616263"
|
||||
assert h(1000 * b"x") == 1000 * b"78"
|
||||
# bytearray not supported yet
|
||||
# assert h(bytearray(b"a")) = b"61"
|
||||
assert binascii.b2a_hex(b"aa") == b"6161"
|
||||
|
||||
with assertRaises(TypeError):
|
||||
h("a")
|
||||
|
||||
|
||||
# unhexlify tests
|
||||
uh = binascii.unhexlify
|
||||
|
||||
assert uh(b"616263") == b"abc"
|
||||
assert uh(1000 * b"78") == 1000 * b"x"
|
||||
x = 1000 * b"1234"
|
||||
assert uh(h(x)) == x
|
||||
assert uh(b"ABCDEF") == b"\xab\xcd\xef"
|
||||
assert binascii.a2b_hex(b"6161") == b"aa"
|
||||
|
||||
# unhexlify on strings not supported yet
|
||||
# assert uh("abcd") == b"\xab\xcd"
|
||||
|
||||
with assertRaises(ValueError):
|
||||
uh(b"a") # Odd-length string
|
||||
|
||||
with assertRaises(ValueError):
|
||||
uh(b"nn") # Non-hexadecimal digit found
|
||||
|
||||
assert binascii.crc32(b"hello world") == 222957957
|
||||
assert binascii.crc32(b"hello world", 555555) == 1216827162
|
||||
assert binascii.crc32(b"goodbye interesting world",777777) == 1885538403
|
||||
Loaded 100 of 215 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user