Merge branch 'master' into coolreader18/demo-repl-continuation

This commit is contained in:
coolreader18 committed 2019-06-08 13:17:05 -05:00
commit cd61248635
215 files changed
+26425 -7564

No files matched your search

+1
View File
@@ -9,3 +9,4 @@ __pycache__
.vscode
wasm-pack.log
.idea/
tests/snippets/resources
Generated
+641 -416
View File
File diff suppressed because it is too large. Load diff
+9 -1
View File
@@ -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
View File
@@ -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.
+10
View File
@@ -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
View File
@@ -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
+196
View File
@@ -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
View File
@@ -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__ = ()
+151
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+135
View File
@@ -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)
+525
View File
@@ -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
-11
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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] != '_']
+1 -1
View File
@@ -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
+19
View File
@@ -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
+125
View File
@@ -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());
})
}
+33
View File
@@ -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
+110
View File
@@ -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)
+31
View File
@@ -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
View File
@@ -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"
+158
View File
@@ -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);
}
}
}
}
+200
View File
@@ -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
View File
@@ -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))
}
+416
View File
@@ -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)
}
-4
View File
@@ -1,4 +0,0 @@
src/python.rs
target/
Cargo.lock
+5 -3
View File
@@ -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
View File
@@ -1,5 +1,5 @@
use lalrpop;
fn main() {
lalrpop::process_root().unwrap();
lalrpop::process_root().unwrap()
}
+27 -10
View File
@@ -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 -2
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+5 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+1
View File
@@ -0,0 +1 @@
snippets/whats_left_to_implement.py
+2 -1
View File
@@ -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
View File
@@ -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.
+12
View File
@@ -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"
+7
View File
@@ -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
+48
View File
@@ -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())
-2
View File
@@ -1,2 +0,0 @@
x = []
x.append(1)
+7
View File
@@ -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
+26
View File
@@ -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
+10
View File
@@ -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()]))
+8 -11
View File
@@ -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()]))
+4 -5
View File
@@ -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
+8
View File
@@ -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)")
+94 -3
View File
@@ -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)
+15
View File
@@ -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)
+7
View File
@@ -0,0 +1,7 @@
import os
from import_file import import_file
import_file()
assert os.path.basename(__file__) == "builtin_file.py"
+5
View File
@@ -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()))
+11 -2
View File
@@ -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))
+16
View File
@@ -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)
+67 -9
View File
@@ -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()
+28
View File
@@ -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
View File
@@ -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')
+599
View File
@@ -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
View File
@@ -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
+23
View File
@@ -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)
+5
View File
@@ -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]
+10
View File
@@ -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()
+9
View File
@@ -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
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
print('Hello')
+108
View File
@@ -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())
+12
View File
@@ -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)
+2
View File
@@ -0,0 +1,2 @@
import __hello__
assert __hello__.initialized == True
+70
View File
@@ -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()
+61 -8
View File
@@ -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)]
+29
View File
@@ -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
+60
View File
@@ -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
+23
View File
@@ -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([])
+28
View File
@@ -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
+1
View File
@@ -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()
+4
View File
@@ -0,0 +1,4 @@
import os
def import_file():
assert os.path.basename(__file__) == "import_file.py"
+4
View File
@@ -0,0 +1,4 @@
# Mutual recursive import:
import import_mutual2
+3
View File
@@ -0,0 +1,3 @@
# Mutual recursive import:
import import_mutual1
+72 -1
View File
@@ -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
+18
View File
@@ -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')
+32
View File
@@ -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))
+1 -2
View File
@@ -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")
+318
View File
@@ -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]
+8
View File
@@ -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
+18
View File
@@ -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__
+53 -11
View File
@@ -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')
+89
View File
@@ -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))
+8 -7
View File
@@ -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():
+5
View File
@@ -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()
+6
View File
@@ -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
View File
@@ -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
+9
View File
@@ -0,0 +1,9 @@
# Test several statement types
# small ones, seperated by ';':
if True:
5;4;
b=4
b;
+39
View File
@@ -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