CapPython's use of unbound methods

Guido asked me to explain why the removal of unbound methods in Python 3.0 causes a problem for enforcing encapsulation in CapPython (an object-capability subset of Python), which I talked about in a blog post [1]. It also came up on python-dev [2]. Let me try a slightly different example to answer Guido's immediate question. Suppose we have an object x with a private attribute, "_field", defined by a class Foo: class Foo(object): def __init__(self): self._field = "secret" x = Foo() Suppose CapPython code is handed x. It should not be able to read x._field, and the expression x._field will be rejected by CapPython's static verifier. However, in Python 3.0, the CapPython code can do this: class C(object): def f(self): return self._field C.f(x) # returns "secret" Whereas in Python 2.x, C.f(x) would raise a TypeError, because C.f is not being called on an instance of C. Guido said, "I don't understand where the function object f gets its magic powers". The answer is that function definitions directly inside class statements are treated specially by the verifier. If you wrote the same function definition at the top level: def f(var): return var._field # rejected the attribute access would be rejected by the verifier, because "var" is not a self variable, and private attributes may only be accessed through self variables. I renamed the variable in the example, but the name of the variable makes no difference to whether it is considered to be a self variable. Self variables are defined as follows: If a function definition "def f(v1, ...)" appears immediately within a "class" statement, the function's first argument, v1, is a self variable, provided that: * the "def" is not preceded by any decorators, and * "f" is not read anywhere in class scope and is not declared as global. The reason for these two restrictions is to prevent the function object from escaping and being used directly. Mark [1] http://lackingrhoticity.blogspot.com/2008/09/cappython-unbound-methods-and-p... [2] http://mail.python.org/pipermail/python-dev/2008-September/082499.html

On Thu, Mar 12, 2009 at 1:24 PM, Mark Seaborn <mrs@mythic-beasts.com> wrote:
Can you add some principals to this example? Who wrote the Foo class definition? Does CapPython have access to the source code for Foo? To the class object?
Suppose CapPython code is handed x.
What does it mean to "hand x to CapPython"? Who "is" CapPython?
In Python 2.x I could write class C(Foo): def f(self): return self._field or alternatively class C(x.__class__): <same f as before>
Hm, this sounds like a major change in language semantics, and if I were Sun I'd sue you for using the name "Python" in your product. :-)
What do you mean by this?
Do you also catch things like g = getattr s = 'field'.replace('f', '_f') print g(x, s) ?
-- --Guido van Rossum (home page: http://www.python.org/~guido/)

Guido van Rossum <guido@python.org> wrote:
OK, suppose we have two principals, Alice and Bob. Alice receives a string from Bob. Alice instantiates the string using CapPython's safe_eval() function, getting back a module object that contains a function object. Alice passes the function an object x. Alice's intention is that the function should not be able to get hold of the contents of x._field, no matter what string Bob supplies. To make this more concrete, this is what Alice executes, with source_from_bob defined in a string literal for the sake of example: source_from_bob = """ class C: def f(self): return self._field def entry_point(x): C.f(x) # potentially gets the secret object in Python 3.0 """ import safeeval secret = object() class Foo(object): def __init__(self): self._field = secret x = Foo() module = safeeval.safe_eval(source_from_bob, safeeval.Environment()) module.entry_point(x) In this example, Bob's code is not given access to the class object Foo. Furthermore, Bob should not be able to get access to the class Foo from the instance x. The type() builtin is not considered to be safe in CapPython so it is not included in the default environment. Bob's code is not given access to the source code for class Foo. But even if Bob is aware of Alice's source code, it should not affect whether Bob can get hold of the secret object. By the way, you can try out the example by getting the code from the Bazaar repository: bzr branch http://bazaar.launchpad.net/%7Emrs/cappython/trunk cappython
In the example above, Bob's code is not given access to Foo, so Bob cannot do this. But you are right, if Bob's code were passed Foo as well as x, Bob could do this. Suppose Alice wanted to give Bob access to class Foo, perhaps so that Bob could create derived classes. It is still possible for Alice to do that safely, if Alice defines Foo differently. Alice can pass the secret object to Foo's constructor instead of having the class definition get its reference to the secret object from an enclosing scope: class Foo(object): def __init__(self, arg): self._field = arg secret = object() x = Foo(secret) module = safeeval.safe_eval(source_from_bob, safeeval.Environment()) module.entry_point(x, Foo) Bob can create his own objects derived from Foo, but cannot use his access to Foo to break encapsulation of instance x. Foo is now authorityless, in the sense that it does not capture "secret" from its enclosing environment, unlike the previous definition.
The verifier would reject x.__class__, so this is not possible.
Damn, the makers of Typed Lambda Calculus had better watch out for legal action from the makers of Lambda Calculus(tm) too... :-) Is it really a major change in semantics if it's just a subset? ;-) To some extent the verifier's check of only accessing private attributes through self is just checking a coding style that I already follow when writing Python code (except sometimes for writing test cases). Of course some of the verifier's checks, such as only allowing attribute assignments through self, are a lot more draconian than coding style checks.
I just mean that I applied alpha conversion. def f(self): return self._field is equivalent to def f(var): return var._field Whether these function definitions are accepted by the verifier depends on their context.
The default environment doesn't provide the real getattr() function. It provides a wrapped version that rejects private attribute names. Mark

On Thu, Mar 19, 2009 at 4:12 PM, Mark Seaborn <mrs@mythic-beasts.com> wrote:
OK, I think I understand all this, except I don't have much of an idea of what subset of the language Bob is allowed to used.
If you don't mind I will try to avoid downloading your source a little longer.
Well yes. The empty subset is also a subset. :-) More seriously, IIUC you are disallowing all use of attribute names starting with underscores, which not only invalidates most Python code in practical use (though you might not care about that) but also disallows the use of many features that are considered part of the language, such as access to __dict__ and many other introspective attributes.
You might wish this to be true, but for most Python programmers, it isn't. Introspection is a commonly-used part of the language (probably more so than in Java). So is the use of attribute names starting with a single underscore outside the class tree, e.g. by "friend" functions.
That also sounds like a rather serious hindrance to writing Python as most people think of it.
BTW that's a new term for me. :-)
This equivalence is good.
Whether these function definitions are accepted by the verifier depends on their context.
But this isn't. Are you saying that the verifier accepts the use of self._foo in a method? That would make the scenario of potentially passing a class defined by Alice into Bob's code much harder to verify -- now suddenly Alice has to know about a lot of things before she can be sure that she doesn't leave open a backdoor for Bob.
Do you have a web page describing the precise list of limitations you apply in your "subset" of Python? Does it support import of some form? -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Le Sun, 22 Mar 2009 15:31:15 -0700, Guido van Rossum <guido@python.org> s'exprima ainsi:
Just a side note. In a language that does not hold a notion of private attribute as core feature, "morphologic" (name forming) convention is a great help. I have long thought a more formal way of introducing public interface -- if only a simple declarative line at top of class def -- would be better, but I recently changed my mind. I think now the privacy vs "publicity" opposition is rather relative, vague, changing. Let's take the case of any toolset/library code with several classes communicating with each other. In most cases, some attributes will be both hidden to client code but exposed to other objects of the toolset. So there are already 3 levels of privacy. If we now introduce tools of the toolset and pure client interface classes we add two levels... Privacy is relative, so to say conventional; in addition to relative levels, there are also qualitative differences in privacy. Some languages (esp. Java) invent hardcoded languages features in a hopeless trial to formalize all of these distinctions. The python way of just saying "let this alone, unless you really know what you intend to do" is probably better to cope with such unclear and variable notions. Denis ------ la vita e estrany

Guido van Rossum <guido@python.org> wrote:
As a side note, it is interesting to compare CapPython to ECMAScript 3.1's strict mode, which, as I understand it, changes the semantics of ECMAScript's attribute access such that doing X.A when X does not have an attribute A raises an exception rather than returning undefined. Since existing Javascript implementations lack this feature, Cajita (a fail-stop subset of Javascript, part of the Caja project) has to go to some lengths to emulate it. This seems to be the main reason that Cajita rewrites Javascript code, to add attribute existence checks. Fortunately CapPython does not have to make this kind of semantic change. Interestingly, in Javascript is is easier to add this kind of change on a per-module basis than in Python, because dynamic attribute access in Javascript is done via a builtin syntax (x[a]) rather than via a function (getattr in Python). However, CPython's restricted execution mode (which Tav is proposing to resurrect) does change the semantics of attribute access. It's not yet clear to me how this works, and how it applies to the getattr function. I suspect it involves looking up the stack.
This is true. I'm not claiming that a lot of Python code will pass the verifier. It might not accept all idiomatic code; I'm just claiming that code using encapsulated objects under CapPython can still be idiomatic. We could probably allow reading self.__dict__ safely in CapPython. The term "introspection" covers a lot of language features. Some are OK in an object-capability language and some are not. For example, some might consider dir() to be an introspective feature, and this function is fine if suitably wrapped. x.__class__.__name__ is a common idiom. Although we can't allow x.__class__ on its own, we could provide a get_class_name function and rewrite "x.__class__.__name__" to "get_class_name(x)". "type(x) is C" is another common idiom. Again, CapPython doesn't provide type() but it can provide a type_is() function: def type_is(x, t): return type(x) is t The "locals" builtin is not something CapPython can allow in general. Any function that can look up the stack in this way is potentially dangerous. But it might be OK to allow "locals()", i.e. the case where "locals" is called as a function and not used as a first class value. I would prefer not to have to do that though.
The friend function pattern is an example of something that CapPython could support, with some extra notation in order to make it explicit. It is a case of what is known as rights amplification in capability systems. Here's an example of how I envisage it would work in CapPython: class C(object): def _get_foo(self): return self._foo _get_foo = C._get_foo Although C._get_foo would normally be rejected, the verifier would allow reading C._get_foo immediately after the class definition as a special case. The resulting _get_foo function would only be able to operate on instances of C (assuming the presence of unbound methods in the language).
Attribute assignment is something that we could handle by rewriting. For example, x.y = z could be rewritten to x.set_attribute("y", z) x's class definition would have to declare that attribute y is assignable. The problem with attribute assignment in Python as it stands is that it is opt-out. Attributes can be made read-only (by using "property" or defining __setattr__), but this is not the default.
Yes.
In most cases Alice would not want Bob to extend classes that she has defined, so she would not give Bob access to the unwrapped class objects. She would just give Bob the constructor. If Alice wants to be sure that she does that, she can add a decorator to all her class definitions: def constructor_only(klass): def wrapper(*args, **kwargs): return klass(*args, **kwargs) return wrapper @constructor_only class C(object): ... (However, this assumes that class decorators are available, and CapPython does not support Python 2.6 yet.)
I started some wiki pages to explain the verifier rules and which builtins are allowed, blocked or wrapped: http://plash.beasts.org/wiki/CapPython/VerifierRules http://plash.beasts.org/wiki/CapPython/Builtins I hope that will make things clearer.
Does it support import of some form?
Yes, it supports import: http://lackingrhoticity.blogspot.com/2008/09/dealing-with-modules-and-builti... The safeeval module allows callers to provide their own __import__ function when evalling code. Mark

On Sun, Mar 29, 2009 at 4:57 PM, Mark Seaborn <mrs@mythic-beasts.com> wrote:
Well of course it makes a much more severe semantic change by declaring illegal all use of attribute names starting with underscore.
I guess if you wanted to override getattr on a per-module basis you could give each module a separate __builtins__.
However, CPython's restricted execution mode (which Tav is proposing to resurrect) does change the semantics of attribute access.
It does not change the general semantics of attribute access -- it only takes away a small set of *specific* attributes (e.g. __code__ and func_code) from a small set of *specific* object types (e.g. function objects). This is because every object has the ability to override getting attributes (via __getattribute__ in Python, or tp_getattro in C).
No, it does not look at the stack. It looks at the globals, which contain a special magic entry __builtins__ (with an 's') which is the dict where built-in functions are looked up. When this dict is the same object as the *default* built-in dict (which is __builtin__.__dict__ where __builtin__ -- without 's' -- is the module defining the built-in functions), it gives you supervisor privileges; if it is any other object, it disallows access to those specific attributes I referred to above. I really recommend that you study the CPython implementation. Without understanding it you stand a chance of creating a secure subset. The getattr() function and the x.y notation both invoke the same implementation (PyObject_GetAttr()). This in turn defers to the tp_getattro slot of the object x. And if the object is implemented in Python, this in turn defers to the object's __getattribute__ method. Then object.__getattribute__ defines the default lookup code, which searches into the object's __dict__ if there is one, then in the class's __dict__ and walking the MRO, and finally (just before raising AttributeError) calls the __getattr__ hook if it exists (don't confuse the latter with __getattribute__).
For some definition of idiomatic. There are a lot of well-known Python idioms involving attribute names starting with underscore. (I hate to question your Python proficiency, but I do have to wonder -- how much Python have you written in your life? Where did you learn Python?)
We could probably allow reading self.__dict__ safely in CapPython.
Though that's not enough -- peeking in other.__dict__ is also somewhat common.
The term "introspection" covers a lot of language features. Some are OK in an object-capability language and some are not.
Agreed. And many introspection features aren't that important or commonly used. But some others are, and this includes using __dict__ and __class__.
For example, some might consider dir() to be an introspective feature,
It is.
and this function is fine if suitably wrapped.
You'd have to look at the C implementation to see what it might do though.
Though in most cases isinstance(x, C) is preferred.
And slowly we slide down the path of writing less and less idiomatic Python...
Using locals() isn't that idiomatic anyway, so this is probably fine. It's mostly used by beginners who are still exploring the extreme end of the language's dynamism. :-)
I'm not sure how useful this is -- friends aren't necessarily in the same module as the class, otherwise they might as well be declared as static methods.
Why not x.set_y(z) ?
This will encourage people to write "Java in Python" which is an unfortunately common anti-pattern.
Or perhaps, better, a factory function, right?
Clever. It does meant that even the class body of C cannot refer to C-the-class, which prevents certain idioms (mostly involving updating class variables -- perhaps not all that common).
(However, this assumes that class decorators are available, and CapPython does not support Python 2.6 yet.)
Well you can always do this manually: class C(object): ... C = constructor_only(C)
Ok, I'll try to remember to look there before responding next time.
Ok. Have you done a security contest like Tav did yet? Implementing import correctly *and* safely is fiendishly difficult. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Thu, Mar 12, 2009 at 1:24 PM, Mark Seaborn <mrs@mythic-beasts.com> wrote:
Can you add some principals to this example? Who wrote the Foo class definition? Does CapPython have access to the source code for Foo? To the class object?
Suppose CapPython code is handed x.
What does it mean to "hand x to CapPython"? Who "is" CapPython?
In Python 2.x I could write class C(Foo): def f(self): return self._field or alternatively class C(x.__class__): <same f as before>
Hm, this sounds like a major change in language semantics, and if I were Sun I'd sue you for using the name "Python" in your product. :-)
What do you mean by this?
Do you also catch things like g = getattr s = 'field'.replace('f', '_f') print g(x, s) ?
-- --Guido van Rossum (home page: http://www.python.org/~guido/)

Guido van Rossum <guido@python.org> wrote:
OK, suppose we have two principals, Alice and Bob. Alice receives a string from Bob. Alice instantiates the string using CapPython's safe_eval() function, getting back a module object that contains a function object. Alice passes the function an object x. Alice's intention is that the function should not be able to get hold of the contents of x._field, no matter what string Bob supplies. To make this more concrete, this is what Alice executes, with source_from_bob defined in a string literal for the sake of example: source_from_bob = """ class C: def f(self): return self._field def entry_point(x): C.f(x) # potentially gets the secret object in Python 3.0 """ import safeeval secret = object() class Foo(object): def __init__(self): self._field = secret x = Foo() module = safeeval.safe_eval(source_from_bob, safeeval.Environment()) module.entry_point(x) In this example, Bob's code is not given access to the class object Foo. Furthermore, Bob should not be able to get access to the class Foo from the instance x. The type() builtin is not considered to be safe in CapPython so it is not included in the default environment. Bob's code is not given access to the source code for class Foo. But even if Bob is aware of Alice's source code, it should not affect whether Bob can get hold of the secret object. By the way, you can try out the example by getting the code from the Bazaar repository: bzr branch http://bazaar.launchpad.net/%7Emrs/cappython/trunk cappython
In the example above, Bob's code is not given access to Foo, so Bob cannot do this. But you are right, if Bob's code were passed Foo as well as x, Bob could do this. Suppose Alice wanted to give Bob access to class Foo, perhaps so that Bob could create derived classes. It is still possible for Alice to do that safely, if Alice defines Foo differently. Alice can pass the secret object to Foo's constructor instead of having the class definition get its reference to the secret object from an enclosing scope: class Foo(object): def __init__(self, arg): self._field = arg secret = object() x = Foo(secret) module = safeeval.safe_eval(source_from_bob, safeeval.Environment()) module.entry_point(x, Foo) Bob can create his own objects derived from Foo, but cannot use his access to Foo to break encapsulation of instance x. Foo is now authorityless, in the sense that it does not capture "secret" from its enclosing environment, unlike the previous definition.
The verifier would reject x.__class__, so this is not possible.
Damn, the makers of Typed Lambda Calculus had better watch out for legal action from the makers of Lambda Calculus(tm) too... :-) Is it really a major change in semantics if it's just a subset? ;-) To some extent the verifier's check of only accessing private attributes through self is just checking a coding style that I already follow when writing Python code (except sometimes for writing test cases). Of course some of the verifier's checks, such as only allowing attribute assignments through self, are a lot more draconian than coding style checks.
I just mean that I applied alpha conversion. def f(self): return self._field is equivalent to def f(var): return var._field Whether these function definitions are accepted by the verifier depends on their context.
The default environment doesn't provide the real getattr() function. It provides a wrapped version that rejects private attribute names. Mark

On Thu, Mar 19, 2009 at 4:12 PM, Mark Seaborn <mrs@mythic-beasts.com> wrote:
OK, I think I understand all this, except I don't have much of an idea of what subset of the language Bob is allowed to used.
If you don't mind I will try to avoid downloading your source a little longer.
Well yes. The empty subset is also a subset. :-) More seriously, IIUC you are disallowing all use of attribute names starting with underscores, which not only invalidates most Python code in practical use (though you might not care about that) but also disallows the use of many features that are considered part of the language, such as access to __dict__ and many other introspective attributes.
You might wish this to be true, but for most Python programmers, it isn't. Introspection is a commonly-used part of the language (probably more so than in Java). So is the use of attribute names starting with a single underscore outside the class tree, e.g. by "friend" functions.
That also sounds like a rather serious hindrance to writing Python as most people think of it.
BTW that's a new term for me. :-)
This equivalence is good.
Whether these function definitions are accepted by the verifier depends on their context.
But this isn't. Are you saying that the verifier accepts the use of self._foo in a method? That would make the scenario of potentially passing a class defined by Alice into Bob's code much harder to verify -- now suddenly Alice has to know about a lot of things before she can be sure that she doesn't leave open a backdoor for Bob.
Do you have a web page describing the precise list of limitations you apply in your "subset" of Python? Does it support import of some form? -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Le Sun, 22 Mar 2009 15:31:15 -0700, Guido van Rossum <guido@python.org> s'exprima ainsi:
Just a side note. In a language that does not hold a notion of private attribute as core feature, "morphologic" (name forming) convention is a great help. I have long thought a more formal way of introducing public interface -- if only a simple declarative line at top of class def -- would be better, but I recently changed my mind. I think now the privacy vs "publicity" opposition is rather relative, vague, changing. Let's take the case of any toolset/library code with several classes communicating with each other. In most cases, some attributes will be both hidden to client code but exposed to other objects of the toolset. So there are already 3 levels of privacy. If we now introduce tools of the toolset and pure client interface classes we add two levels... Privacy is relative, so to say conventional; in addition to relative levels, there are also qualitative differences in privacy. Some languages (esp. Java) invent hardcoded languages features in a hopeless trial to formalize all of these distinctions. The python way of just saying "let this alone, unless you really know what you intend to do" is probably better to cope with such unclear and variable notions. Denis ------ la vita e estrany

Guido van Rossum <guido@python.org> wrote:
As a side note, it is interesting to compare CapPython to ECMAScript 3.1's strict mode, which, as I understand it, changes the semantics of ECMAScript's attribute access such that doing X.A when X does not have an attribute A raises an exception rather than returning undefined. Since existing Javascript implementations lack this feature, Cajita (a fail-stop subset of Javascript, part of the Caja project) has to go to some lengths to emulate it. This seems to be the main reason that Cajita rewrites Javascript code, to add attribute existence checks. Fortunately CapPython does not have to make this kind of semantic change. Interestingly, in Javascript is is easier to add this kind of change on a per-module basis than in Python, because dynamic attribute access in Javascript is done via a builtin syntax (x[a]) rather than via a function (getattr in Python). However, CPython's restricted execution mode (which Tav is proposing to resurrect) does change the semantics of attribute access. It's not yet clear to me how this works, and how it applies to the getattr function. I suspect it involves looking up the stack.
This is true. I'm not claiming that a lot of Python code will pass the verifier. It might not accept all idiomatic code; I'm just claiming that code using encapsulated objects under CapPython can still be idiomatic. We could probably allow reading self.__dict__ safely in CapPython. The term "introspection" covers a lot of language features. Some are OK in an object-capability language and some are not. For example, some might consider dir() to be an introspective feature, and this function is fine if suitably wrapped. x.__class__.__name__ is a common idiom. Although we can't allow x.__class__ on its own, we could provide a get_class_name function and rewrite "x.__class__.__name__" to "get_class_name(x)". "type(x) is C" is another common idiom. Again, CapPython doesn't provide type() but it can provide a type_is() function: def type_is(x, t): return type(x) is t The "locals" builtin is not something CapPython can allow in general. Any function that can look up the stack in this way is potentially dangerous. But it might be OK to allow "locals()", i.e. the case where "locals" is called as a function and not used as a first class value. I would prefer not to have to do that though.
The friend function pattern is an example of something that CapPython could support, with some extra notation in order to make it explicit. It is a case of what is known as rights amplification in capability systems. Here's an example of how I envisage it would work in CapPython: class C(object): def _get_foo(self): return self._foo _get_foo = C._get_foo Although C._get_foo would normally be rejected, the verifier would allow reading C._get_foo immediately after the class definition as a special case. The resulting _get_foo function would only be able to operate on instances of C (assuming the presence of unbound methods in the language).
Attribute assignment is something that we could handle by rewriting. For example, x.y = z could be rewritten to x.set_attribute("y", z) x's class definition would have to declare that attribute y is assignable. The problem with attribute assignment in Python as it stands is that it is opt-out. Attributes can be made read-only (by using "property" or defining __setattr__), but this is not the default.
Yes.
In most cases Alice would not want Bob to extend classes that she has defined, so she would not give Bob access to the unwrapped class objects. She would just give Bob the constructor. If Alice wants to be sure that she does that, she can add a decorator to all her class definitions: def constructor_only(klass): def wrapper(*args, **kwargs): return klass(*args, **kwargs) return wrapper @constructor_only class C(object): ... (However, this assumes that class decorators are available, and CapPython does not support Python 2.6 yet.)
I started some wiki pages to explain the verifier rules and which builtins are allowed, blocked or wrapped: http://plash.beasts.org/wiki/CapPython/VerifierRules http://plash.beasts.org/wiki/CapPython/Builtins I hope that will make things clearer.
Does it support import of some form?
Yes, it supports import: http://lackingrhoticity.blogspot.com/2008/09/dealing-with-modules-and-builti... The safeeval module allows callers to provide their own __import__ function when evalling code. Mark

On Sun, Mar 29, 2009 at 4:57 PM, Mark Seaborn <mrs@mythic-beasts.com> wrote:
Well of course it makes a much more severe semantic change by declaring illegal all use of attribute names starting with underscore.
I guess if you wanted to override getattr on a per-module basis you could give each module a separate __builtins__.
However, CPython's restricted execution mode (which Tav is proposing to resurrect) does change the semantics of attribute access.
It does not change the general semantics of attribute access -- it only takes away a small set of *specific* attributes (e.g. __code__ and func_code) from a small set of *specific* object types (e.g. function objects). This is because every object has the ability to override getting attributes (via __getattribute__ in Python, or tp_getattro in C).
No, it does not look at the stack. It looks at the globals, which contain a special magic entry __builtins__ (with an 's') which is the dict where built-in functions are looked up. When this dict is the same object as the *default* built-in dict (which is __builtin__.__dict__ where __builtin__ -- without 's' -- is the module defining the built-in functions), it gives you supervisor privileges; if it is any other object, it disallows access to those specific attributes I referred to above. I really recommend that you study the CPython implementation. Without understanding it you stand a chance of creating a secure subset. The getattr() function and the x.y notation both invoke the same implementation (PyObject_GetAttr()). This in turn defers to the tp_getattro slot of the object x. And if the object is implemented in Python, this in turn defers to the object's __getattribute__ method. Then object.__getattribute__ defines the default lookup code, which searches into the object's __dict__ if there is one, then in the class's __dict__ and walking the MRO, and finally (just before raising AttributeError) calls the __getattr__ hook if it exists (don't confuse the latter with __getattribute__).
For some definition of idiomatic. There are a lot of well-known Python idioms involving attribute names starting with underscore. (I hate to question your Python proficiency, but I do have to wonder -- how much Python have you written in your life? Where did you learn Python?)
We could probably allow reading self.__dict__ safely in CapPython.
Though that's not enough -- peeking in other.__dict__ is also somewhat common.
The term "introspection" covers a lot of language features. Some are OK in an object-capability language and some are not.
Agreed. And many introspection features aren't that important or commonly used. But some others are, and this includes using __dict__ and __class__.
For example, some might consider dir() to be an introspective feature,
It is.
and this function is fine if suitably wrapped.
You'd have to look at the C implementation to see what it might do though.
Though in most cases isinstance(x, C) is preferred.
And slowly we slide down the path of writing less and less idiomatic Python...
Using locals() isn't that idiomatic anyway, so this is probably fine. It's mostly used by beginners who are still exploring the extreme end of the language's dynamism. :-)
I'm not sure how useful this is -- friends aren't necessarily in the same module as the class, otherwise they might as well be declared as static methods.
Why not x.set_y(z) ?
This will encourage people to write "Java in Python" which is an unfortunately common anti-pattern.
Or perhaps, better, a factory function, right?
Clever. It does meant that even the class body of C cannot refer to C-the-class, which prevents certain idioms (mostly involving updating class variables -- perhaps not all that common).
(However, this assumes that class decorators are available, and CapPython does not support Python 2.6 yet.)
Well you can always do this manually: class C(object): ... C = constructor_only(C)
Ok, I'll try to remember to look there before responding next time.
Ok. Have you done a security contest like Tav did yet? Implementing import correctly *and* safely is fiendishly difficult. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
participants (3)
-
Guido van Rossum
-
Mark Seaborn
-
spir