Python is a fairly old, mature language.
What features would you have been especially excited about?
Python is a fairly old, mature language.
What features would you have been especially excited about?
I'm also looking forward to PEP-554 [0], which allows for "subinterpreters" for running concurrent code without removing the GIL or incurring the overhead of subprocesses.
If anything, user-input strings must be treated as tainted whatever the case.
1) existing uses of .format() would break:
aifc.py: raise Error('marker {0!r} does not exist'.format(id))
2) existing uses of "%" formatting would break: argparse.py: result = '{%s}' % ','.join(choice_strs)
3) many regular expressions would break: uuid.py: if re.fullmatch('(?:[0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]', value):
4) existing strings which contain uuids would break: uuid.py: >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')1) customizable templates (can't allow the user to access arbitrary variables)
2) i18n (can't allow the translator to access arbitrary variables)
3) complex values being passed to .format() instead of some variable
The current extension module API encourages global state, e.g. types (and objects too) are allocated statically in a global C variable. For example, there is a global C variable `_Py_NoneStruct` that is the Python `None` value, and extension modules are accessing this variable directly.
Every use of this object needs to adjust its reference count, and that reference count is directly stored within the C global variable.
`_Py_NoneStruct` is currently even exposed in the PEP 384 stable ABI. Existing extension module binaries are commonly directly touching `_Py_NoneStruct.ob_refcnt` without any synchronization. Breaking the PEP 384 compatibility promise is fundamentally unavoidable here.
One of two things must happen: Alternative one: All refcount operations must be made atomic for thread-safety. These are really really common in the Python interpreter, but atomic operations are expensive on modern CPUs (especially if there's contention). But multiple threads using the value `None` would be quite common in Python code, so I doubt you'd gain any speed even at today's core counts -- in fact I'd expect the constant inter-CPU-communication for the refcounts to make everything slower than just using a single core with today's Python!
So alternative two, ensure no Python objects are shared between the subinterpreters. That's the plan. But that also means it's a breaking change for extension modules. And it's not just an ABI change (which would be handled by merely recompiling against the new headers). Any extension modules that do not yet support PEP 489 are already incompatible with subinterpreters, so that will take quite a bit of work until the ecosystem is upgraded. But there will probably also be some other breaking API changes. I think type objects are currently shared across subinterpreters, and those are frequently defined as a `Py_TypeObject` global variable in extension module code. Also, if every subinterpreter has its own GIL, extension modules calling `PyGILState_Ensure()` will have to specify which subinterpreter they will be using, so that the appropriate lock can be acquired.
My prediction: 3.9 may have the basic functionality, but it still won't be able to run on multiple cores concurrently. That will take a bunch of more work and breaking changes, and will likely be released as Python 4.0.
There will be another slow upgrade process ("my dependencies must upgrade before I can") until the Python ecosystem is multi-subinterpreter-compatible. But at least this one only affects extension modules.
I wonder if there is anything that can really be done, outside of something like Cython.
I compile my cli tools with nuitka [0], the resulting binaries take half the time to start. I find the difference quite notable.
But startup time increases an order of magnitude once you start importing 'requests' and other common packages.
Perhaps an option to turn imports lazy...
I've started using Docker for ensuring prod behaves the same as dev. Going from Mac dev to Linux prod would otherwise cause trouble.
Does your container expose ports or need volumes? Does it need gunicorn or uwsgi in front of it? What about system packages? None of those (except maybe the last one) are really in the scope of the package manager.
If you use setuptools and place all your configuration declaratively in setup.cfg it is not that bad.
Python's heuristics for that are pretty annoying.
If it's any consolation, the scope of a variable isn't determined by heuristics, it's just that the rules are (IMO) kinda bad. Basically, if a variable is assigned (in addition to `=` and friends, `import`, `class` and `def` are also assignments in disguise) to in what Python calls a "block" (which is not like a C-style block; rather `class` body, `def` body, or top-level) it is considered to belong to that block (with the notable exception of the name for a caught exception in a `except Exception as e: ...`, see [1]). If you want it to refer to a variable outside the block, you need to use `global` or `nonlocal` as appropriate. The full gory details are in [2]
1: https://docs.python.org/3/reference/compound_stmts.html#the-...
2: https://docs.python.org/3/reference/executionmodel.html#nami...
Python didn't use to have lexical scoping, so the rules are retrofitting around that.
"end" statement which would enable automatic indentation.
"switch" statement instead of "if/elif/.../else" hell.
Correctly done multi-line lambdas/expressions could bring Python's expressivitiy to where ECMAScript is nowadays, where you can write both foo = function(...) or function foo().
Perl 6 has a great switch statement, called "given" https://docs.perl6.org/language/control#given
You can use mypy for that: https://github.com/python/mypy
Emacs Lisp, at least, can use question marks in names without batting an eyelash.
I see no reason that would not have been true for Lisps generally since their birth.
[1] http://www.lispworks.com/documentation/HyperSpec/Body/f_list...