On Fri, Jul 26, 2019 at 01:37:29PM -0700, Andrew Barnert wrote:

> It’s not clear to me whether people want a module-like, class-like, or 
> function-like namespace here, but I do think it’s probably exactly one 
> of those three. And it’ll be a lot easier to define and implement—and 
> for everyone to understand future Python code—if it is.

I'm not going to speak for "people", but for me, I think that the answer 
should be obvious: it is module-like.

I'm not even sure what you mean by "function-like" or "class-like" 
namespaces. Functions aren't namespaces (except in the sense that they 
are objects with a __dict__ and so support attributes): you can't access 
a function locals from the outside. Classes are namespaces, but if you 
want a namespace that behaves like a class, use a class.

For me, the idea is to (optionally) decouple module objects from .py 
files. Of course most of the time when you want a module object, it is 
most useful to split the code out into a seperate file, and that won't 
change.

But there are times when you have a relatively small amount of code that 
cries to be split off into a seperate namespace, but shifting it into a 
seperate physical file is inconvenient. Going from one physical file to 
two physical files is a significant jump in project complexity that's 
not always worth the cost.

So a namespace object that behaves like a module but doesn't need to 
split off into a seperate .py file would be helpful.

The code in a namespace object ought to behave precisely the same as 
if it were copied into a seperate file. (*Almost* -- see below.)

That is, given some module file containing code:

# module.py
code


I should be able to copy the content of the file ("code"), and paste it 
into another file like this:

# another.py
with Namespace("module") as module:
    # paste and indent here
    code


and the resulting namespace object bound to the name "module" should be 
(almost) identical to what you would have got from calling "import 
module" in the first place.

A few possible differences:

1. It isn't clear what the namespace __file__ and __package__ attributes
   ought to contain, or if it should have them at all.

2. For introspection purposes, and to allow for possible future 
   changes, it might be wise to use a subclass of ModuleType for 
   namespace objects.

3. The repr will probably be different.

One more difference needs a bit of explanation:

The standard scoping rule used by Python is LEGB, which goes:

- Local
- Enclosing functions (non-local/closures)
- Global (module)
- Builtins

with a slight variation on that for code inside classes. So when you run 
code in module.py, the global namespace it sees is module.py i.e. 
itself.

But consider what happens when you copy the code from module.py and 
paste it into another file, into a namespace. It would be surprising if 
the code inside the namespace with block couldn't see module level 
globals. So we need a slight variation on the scoping rule:

- Local
- Enclosing functions (non-local/closures)
- Namespace
- Global (module)
- Builtins

(and an analogous change for when classes are involved). To make it more 
clear with a concrete example:


a = 1
b = 2
c = 3
with Namespace("ns") as ns:
    a = 100
    b = 200
    def function():
        a = 999
        return a, b, c
    

ns.function() ought to return (999, 200, 3).

This implies a small difference in behaviour for code in a namespace 
versus a seperate physical file. If I copied that code block out of ns 
above, and pasted it into a physical file, then running function() would 
raise:

    NameError: name 'c' is not defined

I think that this slight difference will be considered desirable, 
unsurprising (it would be surprising if namespaces didn't see their 
surrounding global scope, and even more surprising if external modules 
could pry into another module!) and hopefully uncontroversial.


> I don’t think anyone cares about fast locals here, and I hope nobody 
> cares about things like super magic.

A function shouldn't become slower just because you move it into a 
namespace. And classes ought to behave the same, including super, 
whether they were defined inside or outside a namespace.


> But the differences in how 
> variables in the namespace interact with functions and classes (and 
> new-kind-of-namespace-things) defined within the namespace do probably 
> matter. And you’d probably want to know what to expect from eval/exec, 
> locals/globals/vars, etc., 

Absolutely! There are a couple of tricky corner cases involving eval 
(and exec?) already, related to comprehensions. They will need to be 
ironed out.

I've spent some time (unsuccessfully) trying to get this to work using a 
class statement:

@namespace
class ns:
    a = 100
    def function():
        print(a)

but I couldn't get the function inside the class to see the surrounding 
"a". If Batuhan Taskaya has solved that problem using a with statement, 
it sounds like there's no technical barriers to starting with a third 
party library, iron out the corner cases, and then consider moving it 
into the std lib.

Or even a keyword:

namespace ns:
   code

which would eliminate the need to repeat ourselves when naming the 
namespace.


> Meanwhile, do we even need to name the namespace?

How do you refer to it without binding it to a name?

Does it need an internal ns.__name__ attribute? I think that would be 
useful for the repr, introspection etc. It is sad that we have to repeat 
ourselves:

with namespace("ns") as ns

but that's a limitation of the language. We could solve it with a 
keyword, but let's get a working, usable prototype first before worrying 
about justifying a keyword.


-- 
Steven
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/6O6I6AFZ2QIKOK3TGSY75OQQDHKBW5AL/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to