Compare commits
4 Commits
801f351333
...
21ca80eef4
Author | SHA1 | Date |
---|---|---|
Ethan Furman | 21ca80eef4 | |
Miss Islington (bot) | cbfcc67170 | |
Terry Jan Reedy | b947b305a6 | |
Victor Stinner | b5cf308de8 |
180
Lib/enum.py
180
Lib/enum.py
|
@ -10,31 +10,41 @@ __all__ = [
|
|||
|
||||
|
||||
def _is_descriptor(obj):
|
||||
"""Returns True if obj is a descriptor, False otherwise."""
|
||||
"""
|
||||
Returns True if obj is a descriptor, False otherwise.
|
||||
"""
|
||||
return (
|
||||
hasattr(obj, '__get__') or
|
||||
hasattr(obj, '__set__') or
|
||||
hasattr(obj, '__delete__'))
|
||||
|
||||
hasattr(obj, '__delete__')
|
||||
)
|
||||
|
||||
def _is_dunder(name):
|
||||
"""Returns True if a __dunder__ name, False otherwise."""
|
||||
return (len(name) > 4 and
|
||||
"""
|
||||
Returns True if a __dunder__ name, False otherwise.
|
||||
"""
|
||||
return (
|
||||
len(name) > 4 and
|
||||
name[:2] == name[-2:] == '__' and
|
||||
name[2] != '_' and
|
||||
name[-3] != '_')
|
||||
|
||||
name[-3] != '_'
|
||||
)
|
||||
|
||||
def _is_sunder(name):
|
||||
"""Returns True if a _sunder_ name, False otherwise."""
|
||||
return (len(name) > 2 and
|
||||
"""
|
||||
Returns True if a _sunder_ name, False otherwise.
|
||||
"""
|
||||
return (
|
||||
len(name) > 2 and
|
||||
name[0] == name[-1] == '_' and
|
||||
name[1:2] != '_' and
|
||||
name[-2:-1] != '_')
|
||||
|
||||
name[-2:-1] != '_'
|
||||
)
|
||||
|
||||
def _make_class_unpicklable(cls):
|
||||
"""Make the given class un-picklable."""
|
||||
"""
|
||||
Make the given class un-picklable.
|
||||
"""
|
||||
def _break_on_call_reduce(self, proto):
|
||||
raise TypeError('%r cannot be pickled' % self)
|
||||
cls.__reduce_ex__ = _break_on_call_reduce
|
||||
|
@ -49,11 +59,11 @@ class auto:
|
|||
|
||||
|
||||
class _EnumDict(dict):
|
||||
"""Track enum member order and ensure member names are not reused.
|
||||
"""
|
||||
Track enum member order and ensure member names are not reused.
|
||||
|
||||
EnumMeta will use the names found in self._member_names as the
|
||||
enumeration member names.
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
@ -63,13 +73,13 @@ class _EnumDict(dict):
|
|||
self._auto_called = False
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""Changes anything not dundered or not a descriptor.
|
||||
"""
|
||||
Changes anything not dundered or not a descriptor.
|
||||
|
||||
If an enum member name is used twice, an error is raised; duplicate
|
||||
values are not checked for.
|
||||
|
||||
Single underscore (sunder) names are reserved.
|
||||
|
||||
"""
|
||||
if _is_sunder(key):
|
||||
if key not in (
|
||||
|
@ -90,7 +100,10 @@ class _EnumDict(dict):
|
|||
self._ignore = value
|
||||
already = set(value) & set(self._member_names)
|
||||
if already:
|
||||
raise ValueError('_ignore_ cannot specify already set names: %r' % (already, ))
|
||||
raise ValueError(
|
||||
'_ignore_ cannot specify already set names: %r'
|
||||
% (already, )
|
||||
)
|
||||
elif _is_dunder(key):
|
||||
if key == '__order__':
|
||||
key = '_order_'
|
||||
|
@ -105,7 +118,12 @@ class _EnumDict(dict):
|
|||
raise TypeError('%r already defined as: %r' % (key, self[key]))
|
||||
if isinstance(value, auto):
|
||||
if value.value == _auto_null:
|
||||
value.value = self._generate_next_value(key, 1, len(self._member_names), self._last_values[:])
|
||||
value.value = self._generate_next_value(
|
||||
key,
|
||||
1,
|
||||
len(self._member_names),
|
||||
self._last_values[:],
|
||||
)
|
||||
self._auto_called = True
|
||||
value = value.value
|
||||
self._member_names.append(key)
|
||||
|
@ -118,9 +136,10 @@ class _EnumDict(dict):
|
|||
# This is also why there are checks in EnumMeta like `if Enum is not None`
|
||||
Enum = None
|
||||
|
||||
|
||||
class EnumMeta(type):
|
||||
"""Metaclass for Enum"""
|
||||
"""
|
||||
Metaclass for Enum
|
||||
"""
|
||||
@classmethod
|
||||
def __prepare__(metacls, cls, bases):
|
||||
# check that previous enum members do not exist
|
||||
|
@ -130,7 +149,9 @@ class EnumMeta(type):
|
|||
# inherit previous flags and _generate_next_value_ function
|
||||
member_type, first_enum = metacls._get_mixins_(cls, bases)
|
||||
if first_enum is not None:
|
||||
enum_dict['_generate_next_value_'] = getattr(first_enum, '_generate_next_value_', None)
|
||||
enum_dict['_generate_next_value_'] = getattr(
|
||||
first_enum, '_generate_next_value_', None,
|
||||
)
|
||||
return enum_dict
|
||||
|
||||
def __new__(metacls, cls, bases, classdict):
|
||||
|
@ -176,9 +197,11 @@ class EnumMeta(type):
|
|||
|
||||
# save DynamicClassAttribute attributes from super classes so we know
|
||||
# if we can take the shortcut of storing members in the class dict
|
||||
dynamic_attributes = {k for c in enum_class.mro()
|
||||
for k, v in c.__dict__.items()
|
||||
if isinstance(v, DynamicClassAttribute)}
|
||||
dynamic_attributes = {
|
||||
k for c in enum_class.mro()
|
||||
for k, v in c.__dict__.items()
|
||||
if isinstance(v, DynamicClassAttribute)
|
||||
}
|
||||
|
||||
# Reverse value->name map for hashable values.
|
||||
enum_class._value2member_map_ = {}
|
||||
|
@ -288,7 +311,8 @@ class EnumMeta(type):
|
|||
return True
|
||||
|
||||
def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1):
|
||||
"""Either returns an existing member, or creates a new enum class.
|
||||
"""
|
||||
Either returns an existing member, or creates a new enum class.
|
||||
|
||||
This method is used both when an enum class is given a value to match
|
||||
to an enumeration member (i.e. Color(3)) and for the functional API
|
||||
|
@ -310,12 +334,18 @@ class EnumMeta(type):
|
|||
not correct, unpickling will fail in some circumstances.
|
||||
|
||||
`type`, if set, will be mixed in as the first base class.
|
||||
|
||||
"""
|
||||
if names is None: # simple value lookup
|
||||
return cls.__new__(cls, value)
|
||||
# otherwise, functional API: we're creating a new Enum type
|
||||
return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start)
|
||||
return cls._create_(
|
||||
value,
|
||||
names,
|
||||
module=module,
|
||||
qualname=qualname,
|
||||
type=type,
|
||||
start=start,
|
||||
)
|
||||
|
||||
def __contains__(cls, member):
|
||||
if not isinstance(member, Enum):
|
||||
|
@ -328,22 +358,23 @@ class EnumMeta(type):
|
|||
# nicer error message when someone tries to delete an attribute
|
||||
# (see issue19025).
|
||||
if attr in cls._member_map_:
|
||||
raise AttributeError(
|
||||
"%s: cannot delete Enum member." % cls.__name__)
|
||||
raise AttributeError("%s: cannot delete Enum member." % cls.__name__)
|
||||
super().__delattr__(attr)
|
||||
|
||||
def __dir__(self):
|
||||
return (['__class__', '__doc__', '__members__', '__module__'] +
|
||||
self._member_names_)
|
||||
return (
|
||||
['__class__', '__doc__', '__members__', '__module__']
|
||||
+ self._member_names_
|
||||
)
|
||||
|
||||
def __getattr__(cls, name):
|
||||
"""Return the enum member matching `name`
|
||||
"""
|
||||
Return the enum member matching `name`
|
||||
|
||||
We use __getattr__ instead of descriptors or inserting into the enum
|
||||
class' __dict__ in order to support `name` and `value` being both
|
||||
properties for enum members (which live in the class' __dict__) and
|
||||
enum members themselves.
|
||||
|
||||
"""
|
||||
if _is_dunder(name):
|
||||
raise AttributeError(name)
|
||||
|
@ -356,6 +387,9 @@ class EnumMeta(type):
|
|||
return cls._member_map_[name]
|
||||
|
||||
def __iter__(cls):
|
||||
"""
|
||||
Returns members in definition order.
|
||||
"""
|
||||
return (cls._member_map_[name] for name in cls._member_names_)
|
||||
|
||||
def __len__(cls):
|
||||
|
@ -363,11 +397,11 @@ class EnumMeta(type):
|
|||
|
||||
@property
|
||||
def __members__(cls):
|
||||
"""Returns a mapping of member name->value.
|
||||
"""
|
||||
Returns a mapping of member name->value.
|
||||
|
||||
This mapping lists all enum members, including aliases. Note that this
|
||||
is a read-only view of the internal mapping.
|
||||
|
||||
"""
|
||||
return MappingProxyType(cls._member_map_)
|
||||
|
||||
|
@ -375,15 +409,18 @@ class EnumMeta(type):
|
|||
return "<enum %r>" % cls.__name__
|
||||
|
||||
def __reversed__(cls):
|
||||
"""
|
||||
Returns members in reverse definition order.
|
||||
"""
|
||||
return (cls._member_map_[name] for name in reversed(cls._member_names_))
|
||||
|
||||
def __setattr__(cls, name, value):
|
||||
"""Block attempts to reassign Enum members.
|
||||
"""
|
||||
Block attempts to reassign Enum members.
|
||||
|
||||
A simple assignment to the class namespace only changes one of the
|
||||
several possible ways to get an Enum member from the Enum class,
|
||||
resulting in an inconsistent Enumeration.
|
||||
|
||||
"""
|
||||
member_map = cls.__dict__.get('_member_map_', {})
|
||||
if name in member_map:
|
||||
|
@ -391,7 +428,8 @@ class EnumMeta(type):
|
|||
super().__setattr__(name, value)
|
||||
|
||||
def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, start=1):
|
||||
"""Convenience method to create a new Enum class.
|
||||
"""
|
||||
Convenience method to create a new Enum class.
|
||||
|
||||
`names` can be:
|
||||
|
||||
|
@ -400,7 +438,6 @@ class EnumMeta(type):
|
|||
* An iterable of member names. Values are incremented by 1 from `start`.
|
||||
* An iterable of (member name, value) pairs.
|
||||
* A mapping of member name -> value pairs.
|
||||
|
||||
"""
|
||||
metacls = cls.__class__
|
||||
bases = (cls, ) if type is None else (type, cls)
|
||||
|
@ -487,15 +524,18 @@ class EnumMeta(type):
|
|||
for chain in bases:
|
||||
for base in chain.__mro__:
|
||||
if issubclass(base, Enum) and base._member_names_:
|
||||
raise TypeError("%s: cannot extend enumeration %r" % (class_name, base.__name__))
|
||||
raise TypeError(
|
||||
"%s: cannot extend enumeration %r"
|
||||
% (class_name, base.__name__)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_mixins_(class_name, bases):
|
||||
"""Returns the type for creating enum members, and the first inherited
|
||||
"""
|
||||
Returns the type for creating enum members, and the first inherited
|
||||
enum class.
|
||||
|
||||
bases: the tuple of bases that was given to __new__
|
||||
|
||||
"""
|
||||
if not bases:
|
||||
return object, Enum
|
||||
|
@ -538,12 +578,12 @@ class EnumMeta(type):
|
|||
|
||||
@staticmethod
|
||||
def _find_new_(classdict, member_type, first_enum):
|
||||
"""Returns the __new__ to be used for creating the enum members.
|
||||
"""
|
||||
Returns the __new__ to be used for creating the enum members.
|
||||
|
||||
classdict: the class dictionary given to __new__
|
||||
member_type: the data type whose __new__ will be used by default
|
||||
first_enum: enumeration to check for an overriding __new__
|
||||
|
||||
"""
|
||||
# now find the correct __new__, checking to see of one was defined
|
||||
# by the user; also check earlier enum classes in case a __new__ was
|
||||
|
@ -583,10 +623,10 @@ class EnumMeta(type):
|
|||
|
||||
|
||||
class Enum(metaclass=EnumMeta):
|
||||
"""Generic enumeration.
|
||||
"""
|
||||
Generic enumeration.
|
||||
|
||||
Derive from this class to define new enumerations.
|
||||
|
||||
"""
|
||||
def __new__(cls, value):
|
||||
# all enum instances are actually created during class construction
|
||||
|
@ -629,6 +669,14 @@ class Enum(metaclass=EnumMeta):
|
|||
raise exc
|
||||
|
||||
def _generate_next_value_(name, start, count, last_values):
|
||||
"""
|
||||
Generate the next value when not given.
|
||||
|
||||
name: the name of the member
|
||||
start: the initial start value or None
|
||||
count: the number of existing members
|
||||
last_value: the last value assigned or None
|
||||
"""
|
||||
for last_value in reversed(last_values):
|
||||
try:
|
||||
return last_value + 1
|
||||
|
@ -649,6 +697,9 @@ class Enum(metaclass=EnumMeta):
|
|||
return "%s.%s" % (self.__class__.__name__, self._name_)
|
||||
|
||||
def __dir__(self):
|
||||
"""
|
||||
Returns all members and all public methods
|
||||
"""
|
||||
added_behavior = [
|
||||
m
|
||||
for cls in self.__class__.mro()
|
||||
|
@ -658,12 +709,15 @@ class Enum(metaclass=EnumMeta):
|
|||
return (['__class__', '__doc__', '__module__'] + added_behavior)
|
||||
|
||||
def __format__(self, format_spec):
|
||||
"""
|
||||
Returns format using actual value type unless __str__ has been overridden.
|
||||
"""
|
||||
# mixed-in Enums should use the mixed-in type's __format__, otherwise
|
||||
# we can get strange results with the Enum name showing up instead of
|
||||
# the value
|
||||
|
||||
# pure Enum branch, or branch with __str__ explicitly overridden
|
||||
str_overridden = type(self).__str__ != Enum.__str__
|
||||
str_overridden = type(self).__str__ not in (Enum.__str__, Flag.__str__)
|
||||
if self._member_type_ is object or str_overridden:
|
||||
cls = str
|
||||
val = str(self)
|
||||
|
@ -705,7 +759,9 @@ def _reduce_ex_by_name(self, proto):
|
|||
return self.name
|
||||
|
||||
class Flag(Enum):
|
||||
"""Support for flags"""
|
||||
"""
|
||||
Support for flags
|
||||
"""
|
||||
|
||||
def _generate_next_value_(name, start, count, last_values):
|
||||
"""
|
||||
|
@ -728,6 +784,9 @@ class Flag(Enum):
|
|||
|
||||
@classmethod
|
||||
def _missing_(cls, value):
|
||||
"""
|
||||
Returns member (possibly creating it) if one can be found for value.
|
||||
"""
|
||||
original_value = value
|
||||
if value < 0:
|
||||
value = ~value
|
||||
|
@ -757,6 +816,9 @@ class Flag(Enum):
|
|||
return pseudo_member
|
||||
|
||||
def __contains__(self, other):
|
||||
"""
|
||||
Returns True if self has at least the same flags set as other.
|
||||
"""
|
||||
if not isinstance(other, self.__class__):
|
||||
raise TypeError(
|
||||
"unsupported operand type(s) for 'in': '%s' and '%s'" % (
|
||||
|
@ -815,10 +877,15 @@ class Flag(Enum):
|
|||
|
||||
|
||||
class IntFlag(int, Flag):
|
||||
"""Support for integer-based Flags"""
|
||||
"""
|
||||
Support for integer-based Flags
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def _missing_(cls, value):
|
||||
"""
|
||||
Returns member (possibly creating it) if one can be found for value.
|
||||
"""
|
||||
if not isinstance(value, int):
|
||||
raise ValueError("%r is not a valid %s" % (value, cls.__name__))
|
||||
new_member = cls._create_pseudo_member_(value)
|
||||
|
@ -826,6 +893,9 @@ class IntFlag(int, Flag):
|
|||
|
||||
@classmethod
|
||||
def _create_pseudo_member_(cls, value):
|
||||
"""
|
||||
Create a composite member iff value contains only members.
|
||||
"""
|
||||
pseudo_member = cls._value2member_map_.get(value, None)
|
||||
if pseudo_member is None:
|
||||
need_to_create = [value]
|
||||
|
@ -880,11 +950,15 @@ class IntFlag(int, Flag):
|
|||
|
||||
|
||||
def _high_bit(value):
|
||||
"""returns index of highest bit, or -1 if value is zero or negative"""
|
||||
"""
|
||||
returns index of highest bit, or -1 if value is zero or negative
|
||||
"""
|
||||
return value.bit_length() - 1
|
||||
|
||||
def unique(enumeration):
|
||||
"""Class decorator for enumerations ensuring unique member values."""
|
||||
"""
|
||||
Class decorator for enumerations ensuring unique member values.
|
||||
"""
|
||||
duplicates = []
|
||||
for name, member in enumeration.__members__.items():
|
||||
if name != member.name:
|
||||
|
@ -897,7 +971,9 @@ def unique(enumeration):
|
|||
return enumeration
|
||||
|
||||
def _decompose(flag, value):
|
||||
"""Extract all members from the value."""
|
||||
"""
|
||||
Extract all members from the value.
|
||||
"""
|
||||
# _decompose is only called if the value is not named
|
||||
not_covered = value
|
||||
negative = value < 0
|
||||
|
|
|
@ -417,7 +417,6 @@ class TestEnum(unittest.TestCase):
|
|||
green = 2
|
||||
blue = 3
|
||||
|
||||
|
||||
def test_enum_with_value_name(self):
|
||||
class Huh(Enum):
|
||||
name = 1
|
||||
|
@ -569,12 +568,15 @@ class TestEnum(unittest.TestCase):
|
|||
class Test1Enum(MyMethodEnum, int, MyStrEnum):
|
||||
One = 1
|
||||
Two = 2
|
||||
self.assertTrue(Test1Enum._member_type_ is int)
|
||||
self.assertEqual(str(Test1Enum.One), 'MyStr')
|
||||
self.assertEqual(format(Test1Enum.One, ''), 'MyStr')
|
||||
#
|
||||
class Test2Enum(MyStrEnum, MyMethodEnum):
|
||||
One = 1
|
||||
Two = 2
|
||||
self.assertEqual(str(Test2Enum.One), 'MyStr')
|
||||
self.assertEqual(format(Test1Enum.One, ''), 'MyStr')
|
||||
|
||||
def test_inherited_data_type(self):
|
||||
class HexInt(int):
|
||||
|
@ -2172,6 +2174,11 @@ class TestFlag(unittest.TestCase):
|
|||
self.assertEqual(repr(~(Open.RO | Open.CE)), '<Open.AC: 3>')
|
||||
self.assertEqual(repr(~(Open.WO | Open.CE)), '<Open.RW: 2>')
|
||||
|
||||
def test_format(self):
|
||||
Perm = self.Perm
|
||||
self.assertEqual(format(Perm.R, ''), 'Perm.R')
|
||||
self.assertEqual(format(Perm.R | Perm.X, ''), 'Perm.R|X')
|
||||
|
||||
def test_or(self):
|
||||
Perm = self.Perm
|
||||
for i in Perm:
|
||||
|
@ -2504,6 +2511,7 @@ class TestIntFlag(unittest.TestCase):
|
|||
|
||||
def test_type(self):
|
||||
Perm = self.Perm
|
||||
self.assertTrue(Perm._member_type_ is int)
|
||||
Open = self.Open
|
||||
for f in Perm:
|
||||
self.assertTrue(isinstance(f, Perm))
|
||||
|
@ -2583,6 +2591,11 @@ class TestIntFlag(unittest.TestCase):
|
|||
self.assertEqual(repr(~(Open.WO | Open.CE)), '<Open.RW: -524290>')
|
||||
self.assertEqual(repr(Open(~4)), '<Open.CE|AC|RW|WO: -5>')
|
||||
|
||||
def test_format(self):
|
||||
Perm = self.Perm
|
||||
self.assertEqual(format(Perm.R, ''), '4')
|
||||
self.assertEqual(format(Perm.R | Perm.X, ''), '5')
|
||||
|
||||
def test_or(self):
|
||||
Perm = self.Perm
|
||||
for i in Perm:
|
||||
|
|
|
@ -53,15 +53,6 @@ Goldschmidt.
|
|||
|
||||
..
|
||||
|
||||
.. bpo: 41910
|
||||
.. date: 2020-10-21-14-40-54
|
||||
.. nonce: CzBMit
|
||||
.. section: Core and Builtins
|
||||
|
||||
Document the default implementation of `object.__eq__`.
|
||||
|
||||
..
|
||||
|
||||
.. bpo: 41984
|
||||
.. date: 2020-10-14-16-19-43
|
||||
.. nonce: SEtKMr
|
||||
|
@ -444,6 +435,15 @@ Fix the URL for the IMAP protocol documents.
|
|||
|
||||
..
|
||||
|
||||
.. bpo: 41910
|
||||
.. date: 2020-10-21-14-40-54
|
||||
.. nonce: CzBMit
|
||||
.. section: Documentation
|
||||
|
||||
Document the default implementation of `object.__eq__`.
|
||||
|
||||
..
|
||||
|
||||
.. bpo: 41774
|
||||
.. date: 2020-09-24-15-35-13
|
||||
.. nonce: 5IqdGP
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Fix encoding name when running a ``.pyc`` file on Windows:
|
||||
:c:func:`PyRun_SimpleFileExFlags()` now uses the correct encoding to decode
|
||||
the filename.
|
|
@ -0,0 +1 @@
|
|||
fix `format()` behavior for `IntFlag`
|
|
@ -63,11 +63,15 @@ extern grammar _PyParser_Grammar; /* From graminit.c */
|
|||
static void flush_io(void);
|
||||
static PyObject *run_mod(mod_ty, PyObject *, PyObject *, PyObject *,
|
||||
PyCompilerFlags *, PyArena *);
|
||||
static PyObject *run_pyc_file(FILE *, const char *, PyObject *, PyObject *,
|
||||
static PyObject *run_pyc_file(FILE *, PyObject *, PyObject *,
|
||||
PyCompilerFlags *);
|
||||
static void err_input(perrdetail *);
|
||||
static void err_free(perrdetail *);
|
||||
static int PyRun_InteractiveOneObjectEx(FILE *, PyObject *, PyCompilerFlags *);
|
||||
static PyObject* pyrun_file(FILE *fp, PyObject *filename, int start,
|
||||
PyObject *globals, PyObject *locals, int closeit,
|
||||
PyCompilerFlags *flags);
|
||||
|
||||
|
||||
/* Parse input from a file and execute it */
|
||||
int
|
||||
|
@ -300,82 +304,89 @@ PyRun_InteractiveOneFlags(FILE *fp, const char *filename_str, PyCompilerFlags *f
|
|||
the file type, and, if we may close it, at the first few bytes. */
|
||||
|
||||
static int
|
||||
maybe_pyc_file(FILE *fp, const char* filename, const char* ext, int closeit)
|
||||
maybe_pyc_file(FILE *fp, PyObject *filename, int closeit)
|
||||
{
|
||||
if (strcmp(ext, ".pyc") == 0)
|
||||
PyObject *ext = PyUnicode_FromString(".pyc");
|
||||
if (ext == NULL) {
|
||||
return -1;
|
||||
}
|
||||
Py_ssize_t endswith = PyUnicode_Tailmatch(filename, ext, 0, PY_SSIZE_T_MAX, +1);
|
||||
Py_DECREF(ext);
|
||||
if (endswith) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Only look into the file if we are allowed to close it, since
|
||||
it then should also be seekable. */
|
||||
if (closeit) {
|
||||
/* Read only two bytes of the magic. If the file was opened in
|
||||
text mode, the bytes 3 and 4 of the magic (\r\n) might not
|
||||
be read as they are on disk. */
|
||||
unsigned int halfmagic = PyImport_GetMagicNumber() & 0xFFFF;
|
||||
unsigned char buf[2];
|
||||
/* Mess: In case of -x, the stream is NOT at its start now,
|
||||
and ungetc() was used to push back the first newline,
|
||||
which makes the current stream position formally undefined,
|
||||
and a x-platform nightmare.
|
||||
Unfortunately, we have no direct way to know whether -x
|
||||
was specified. So we use a terrible hack: if the current
|
||||
stream position is not 0, we assume -x was specified, and
|
||||
give up. Bug 132850 on SourceForge spells out the
|
||||
hopelessness of trying anything else (fseek and ftell
|
||||
don't work predictably x-platform for text-mode files).
|
||||
*/
|
||||
int ispyc = 0;
|
||||
if (ftell(fp) == 0) {
|
||||
if (fread(buf, 1, 2, fp) == 2 &&
|
||||
((unsigned int)buf[1]<<8 | buf[0]) == halfmagic)
|
||||
ispyc = 1;
|
||||
rewind(fp);
|
||||
}
|
||||
return ispyc;
|
||||
if (!closeit) {
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
|
||||
/* Read only two bytes of the magic. If the file was opened in
|
||||
text mode, the bytes 3 and 4 of the magic (\r\n) might not
|
||||
be read as they are on disk. */
|
||||
unsigned int halfmagic = PyImport_GetMagicNumber() & 0xFFFF;
|
||||
unsigned char buf[2];
|
||||
/* Mess: In case of -x, the stream is NOT at its start now,
|
||||
and ungetc() was used to push back the first newline,
|
||||
which makes the current stream position formally undefined,
|
||||
and a x-platform nightmare.
|
||||
Unfortunately, we have no direct way to know whether -x
|
||||
was specified. So we use a terrible hack: if the current
|
||||
stream position is not 0, we assume -x was specified, and
|
||||
give up. Bug 132850 on SourceForge spells out the
|
||||
hopelessness of trying anything else (fseek and ftell
|
||||
don't work predictably x-platform for text-mode files).
|
||||
*/
|
||||
int ispyc = 0;
|
||||
if (ftell(fp) == 0) {
|
||||
if (fread(buf, 1, 2, fp) == 2 &&
|
||||
((unsigned int)buf[1]<<8 | buf[0]) == halfmagic)
|
||||
ispyc = 1;
|
||||
rewind(fp);
|
||||
}
|
||||
return ispyc;
|
||||
}
|
||||
|
||||
static int
|
||||
set_main_loader(PyObject *d, const char *filename, const char *loader_name)
|
||||
{
|
||||
PyObject *filename_obj, *bootstrap, *loader_type = NULL, *loader;
|
||||
int result = 0;
|
||||
|
||||
filename_obj = PyUnicode_DecodeFSDefault(filename);
|
||||
if (filename_obj == NULL)
|
||||
return -1;
|
||||
static int
|
||||
set_main_loader(PyObject *d, PyObject *filename, const char *loader_name)
|
||||
{
|
||||
PyInterpreterState *interp = _PyInterpreterState_Get();
|
||||
bootstrap = PyObject_GetAttrString(interp->importlib,
|
||||
"_bootstrap_external");
|
||||
if (bootstrap != NULL) {
|
||||
loader_type = PyObject_GetAttrString(bootstrap, loader_name);
|
||||
Py_DECREF(bootstrap);
|
||||
}
|
||||
if (loader_type == NULL) {
|
||||
Py_DECREF(filename_obj);
|
||||
PyObject *bootstrap = PyObject_GetAttrString(interp->importlib,
|
||||
"_bootstrap_external");
|
||||
if (bootstrap == NULL) {
|
||||
return -1;
|
||||
}
|
||||
loader = PyObject_CallFunction(loader_type, "sN", "__main__", filename_obj);
|
||||
|
||||
PyObject *loader_type = PyObject_GetAttrString(bootstrap, loader_name);
|
||||
Py_DECREF(bootstrap);
|
||||
if (loader_type == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *loader = PyObject_CallFunction(loader_type,
|
||||
"sO", "__main__", filename);
|
||||
Py_DECREF(loader_type);
|
||||
if (loader == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (PyDict_SetItemString(d, "__loader__", loader) < 0) {
|
||||
result = -1;
|
||||
Py_DECREF(loader);
|
||||
return -1;
|
||||
}
|
||||
Py_DECREF(loader);
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
|
||||
PyCompilerFlags *flags)
|
||||
|
||||
static int
|
||||
pyrun_simple_file(FILE *fp, PyObject *filename, int closeit,
|
||||
PyCompilerFlags *flags)
|
||||
{
|
||||
PyObject *m, *d, *v;
|
||||
const char *ext;
|
||||
int set_file_name = 0, ret = -1;
|
||||
size_t len;
|
||||
|
||||
m = PyImport_AddModule("__main__");
|
||||
if (m == NULL)
|
||||
|
@ -383,29 +394,29 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
|
|||
Py_INCREF(m);
|
||||
d = PyModule_GetDict(m);
|
||||
if (PyDict_GetItemString(d, "__file__") == NULL) {
|
||||
PyObject *f;
|
||||
f = PyUnicode_DecodeFSDefault(filename);
|
||||
if (f == NULL)
|
||||
goto done;
|
||||
if (PyDict_SetItemString(d, "__file__", f) < 0) {
|
||||
Py_DECREF(f);
|
||||
if (PyDict_SetItemString(d, "__file__", filename) < 0) {
|
||||
goto done;
|
||||
}
|
||||
if (PyDict_SetItemString(d, "__cached__", Py_None) < 0) {
|
||||
Py_DECREF(f);
|
||||
goto done;
|
||||
}
|
||||
set_file_name = 1;
|
||||
Py_DECREF(f);
|
||||
}
|
||||
len = strlen(filename);
|
||||
ext = filename + len - (len > 4 ? 4 : 0);
|
||||
if (maybe_pyc_file(fp, filename, ext, closeit)) {
|
||||
|
||||
int pyc = maybe_pyc_file(fp, filename, closeit);
|
||||
if (pyc < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (pyc) {
|
||||
FILE *pyc_fp;
|
||||
/* Try to run a pyc file. First, re-open in binary */
|
||||
if (closeit)
|
||||
if (closeit) {
|
||||
fclose(fp);
|
||||
if ((pyc_fp = _Py_fopen(filename, "rb")) == NULL) {
|
||||
}
|
||||
|
||||
pyc_fp = _Py_fopen_obj(filename, "rb");
|
||||
if (pyc_fp == NULL) {
|
||||
fprintf(stderr, "python: Can't reopen .pyc file\n");
|
||||
goto done;
|
||||
}
|
||||
|
@ -416,17 +427,17 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
|
|||
fclose(pyc_fp);
|
||||
goto done;
|
||||
}
|
||||
v = run_pyc_file(pyc_fp, filename, d, d, flags);
|
||||
v = run_pyc_file(pyc_fp, d, d, flags);
|
||||
} else {
|
||||
/* When running from stdin, leave __main__.__loader__ alone */
|
||||
if (strcmp(filename, "<stdin>") != 0 &&
|
||||
if (PyUnicode_CompareWithASCIIString(filename, "<stdin>") != 0 &&
|
||||
set_main_loader(d, filename, "SourceFileLoader") < 0) {
|
||||
fprintf(stderr, "python: failed to set __main__.__loader__\n");
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d,
|
||||
closeit, flags);
|
||||
v = pyrun_file(fp, filename, Py_file_input, d, d,
|
||||
closeit, flags);
|
||||
}
|
||||
flush_io();
|
||||
if (v == NULL) {
|
||||
|
@ -449,6 +460,21 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
|
||||
PyCompilerFlags *flags)
|
||||
{
|
||||
PyObject *filename_obj = PyUnicode_DecodeFSDefault(filename);
|
||||
if (filename_obj == NULL) {
|
||||
return -1;
|
||||
}
|
||||
int res = pyrun_simple_file(fp, filename_obj, closeit, flags);
|
||||
Py_DECREF(filename_obj);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags)
|
||||
{
|
||||
|
@ -1036,39 +1062,53 @@ PyRun_StringFlags(const char *str, int start, PyObject *globals,
|
|||
return ret;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyRun_FileExFlags(FILE *fp, const char *filename_str, int start, PyObject *globals,
|
||||
PyObject *locals, int closeit, PyCompilerFlags *flags)
|
||||
|
||||
static PyObject *
|
||||
pyrun_file(FILE *fp, PyObject *filename, int start, PyObject *globals,
|
||||
PyObject *locals, int closeit, PyCompilerFlags *flags)
|
||||
{
|
||||
PyObject *ret = NULL;
|
||||
PyArena *arena = PyArena_New();
|
||||
if (arena == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mod_ty mod;
|
||||
PyArena *arena = NULL;
|
||||
PyObject *filename;
|
||||
|
||||
filename = PyUnicode_DecodeFSDefault(filename_str);
|
||||
if (filename == NULL)
|
||||
goto exit;
|
||||
|
||||
arena = PyArena_New();
|
||||
if (arena == NULL)
|
||||
goto exit;
|
||||
|
||||
mod = PyParser_ASTFromFileObject(fp, filename, NULL, start, 0, 0,
|
||||
flags, NULL, arena);
|
||||
if (closeit)
|
||||
if (closeit) {
|
||||
fclose(fp);
|
||||
if (mod == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
ret = run_mod(mod, filename, globals, locals, flags, arena);
|
||||
|
||||
exit:
|
||||
Py_XDECREF(filename);
|
||||
if (arena != NULL)
|
||||
PyArena_Free(arena);
|
||||
PyObject *ret;
|
||||
if (mod != NULL) {
|
||||
ret = run_mod(mod, filename, globals, locals, flags, arena);
|
||||
}
|
||||
else {
|
||||
ret = NULL;
|
||||
}
|
||||
PyArena_Free(arena);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals,
|
||||
PyObject *locals, int closeit, PyCompilerFlags *flags)
|
||||
{
|
||||
PyObject *filename_obj = PyUnicode_DecodeFSDefault(filename);
|
||||
if (filename_obj == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *res = pyrun_file(fp, filename_obj, start, globals,
|
||||
locals, closeit, flags);
|
||||
Py_DECREF(filename_obj);
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
flush_io(void)
|
||||
{
|
||||
|
@ -1150,8 +1190,8 @@ run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals,
|
|||
}
|
||||
|
||||
static PyObject *
|
||||
run_pyc_file(FILE *fp, const char *filename, PyObject *globals,
|
||||
PyObject *locals, PyCompilerFlags *flags)
|
||||
run_pyc_file(FILE *fp, PyObject *globals, PyObject *locals,
|
||||
PyCompilerFlags *flags)
|
||||
{
|
||||
PyCodeObject *co;
|
||||
PyObject *v;
|
||||
|
|
Loading…
Reference in New Issue