Compare commits

...

4 Commits

Author SHA1 Message Date
Ethan Furman 21ca80eef4
[3.8] [Enum] reformat and add doc strings (GH-23705). (GH-23706)
* [3.8] [Enum] reformat and add doc strings (GH-23705).
2020-12-08 13:28:47 -08:00
Miss Islington (bot) cbfcc67170
bpo-41907: [Enum] fix format() behavior for IntFlag (GH-22497) (GH-23704)
(cherry picked from commit 37440eef7f)
2020-12-08 11:52:58 -08:00
Terry Jan Reedy b947b305a6
[3.8] bpo-41910: move news entry (GH-23698) 2020-12-08 13:00:03 -05:00
Victor Stinner b5cf308de8
bpo-32381: Fix PyRun_SimpleFileExFlags() encoding (GH-23642) (GH-23692) (GH-23696)
Fix encoding name when running a ".pyc" file on Windows:
PyRun_SimpleFileExFlags() now uses the correct encoding to decode the
filename.

* Add pyrun_file() subfunction.
* Add pyrun_simple_file() subfunction.
* PyRun_SimpleFileExFlags() now calls _Py_fopen_obj() rather than
  _Py_fopen().

(cherry picked from commit b6d98c10ff)
(cherry picked from commit f0e42ae03c)
2020-12-08 17:42:31 +01:00
6 changed files with 290 additions and 157 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
fix `format()` behavior for `IntFlag`

View File

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