#🔒 The point of __build_class__?

29 messages · Page 1 of 1 (latest)

rose umbra
#

I was wondering if any one knows what the point of __build_class__ is? I am assuming it's similar to the point of __import__ which the docs actually says is for Python interpreter. But __build_class__ just says it's an internal helper function.

wise phoenixBOT
#

@rose umbra

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

urban crag
#

The actual implementation depends on the python interpreter that's being used, but this generally handles actually building the class and returning it for the current namespace

steel yew
#

!e py import dis dis.dis(''' class Example: def do_thing(self): print("hello") ''')

wise phoenixBOT
#

@steel yew :white_check_mark: Your 3.12 eval job has completed with return code 0.

001 |   0           0 RESUME                   0
002 | 
003 |   2           2 PUSH_NULL
004 |               4 LOAD_BUILD_CLASS
005 |               6 LOAD_CONST               0 (<code object Example at 0x7f96dbc09a70, file "<dis>", line 2>)
006 |               8 MAKE_FUNCTION            0
007 |              10 LOAD_CONST               1 ('Example')
008 |              12 CALL                     2
009 |              20 STORE_NAME               0 (Example)
010 |              22 RETURN_CONST             2 (None)
011 | 
... (truncated - too many lines)

Full output: https://paste.pythondiscord.com/QTRABHSRYD2A5ZCW46WWLYNARY

steel yew
#

oh right yeah of course it gets truncated

#
  0           0 RESUME                   0

  2           2 PUSH_NULL
              4 LOAD_BUILD_CLASS
              6 LOAD_CONST               0 (<code object Example at 0x7f96dbc09a70, file "<dis>", line 2>)
              8 MAKE_FUNCTION            0
             10 LOAD_CONST               1 ('Example')
             12 CALL                     2
             20 STORE_NAME               0 (Example)
             22 RETURN_CONST             2 (None)

Disassembly of <code object Example at 0x7f96dbc09a70, file "<dis>", line 2>:
  2           0 RESUME                   0
              2 LOAD_NAME                0 (__name__)
              4 STORE_NAME               1 (__module__)
              6 LOAD_CONST               0 ('Example')
              8 STORE_NAME               2 (__qualname__)

  3          10 LOAD_CONST               1 (<code object do_thing at 0x7f96dbc0a5d0, file "<dis>", line 3>)
             12 MAKE_FUNCTION            0
             14 STORE_NAME               3 (do_thing)
             16 RETURN_CONST             2 (None)

Disassembly of <code object do_thing at 0x7f96dbc0a5d0, file "<dis>", line 3>:
  3           0 RESUME                   0

  4           2 LOAD_GLOBAL              1 (NULL + print)
             12 LOAD_CONST               1 ('hello')
             14 CALL                     1
             22 POP_TOP
             24 RETURN_CONST             0 (None)``` here's the full output
#

LOAD_BUILD_CLASS is a special instruction that just gets you the function __build_class__

#

so you can see that what making a class actually does is something more like this py def Example(): __module__ = __name__ __qualname__ = "Example" def do_thing(self): print("hello") Example = __build_class__(Example, "Example")

#

as for why it does that... well it's interpreter implementation details so it is ultimately just "they decided to do it that way", but also like, it makes sense? the block of code inside a class has to get ran inside a special namespace because of the magic thing, and making a separate code object is the easiest way to produce a new namespace

#

turning it into a function might not seem particularly necessary but it causes it to capture the scope around it, where a code object by itself is a constant and would not be able to access variables outside itself

#

and then making __build_class__ a function instead of just a BUILD_CLASS instruction... ok i have no idea what the rationale is behind that one

#

possibly it's so you can supply your own if for some reason you want to hook all the classes? idk

strong pagoda
#

^ every language feature has a backing function.

rose umbra
#

My main interest for this is that I am making some programming games, and of course I need to sandbox the inputs. And I've been experimenting with "unlocks" like the ability to allow the player to make functions or classes. Also I know I'll have to restrict import.

tender bluff
#

Guido mentioned in that mailing list post above it’s so that it can easily reuse all the logic for *args, **kwargs etc.

strong pagoda
#

take a look at runpy

rose umbra
#

Soo... I probably do not really need __build_class__ for anything?

strong pagoda
#

no. It's an internal function.

tender bluff
#

Trying to sandbox inside Python code really isn’t a good idea. The language is so introspectable that it’s very easy to break out of those.

#

Really the best solution is to put the code in like a VM, or maybe run a web assembly build.

strong pagoda
#

^ you typically want to use subprocess with a jail environment

#

!d os.chroot

wise phoenixBOT
#

os.chroot(path)```
Change the root directory of the current process to *path*.

[Availability](https://docs.python.org/3/library/intro.html#availability): Unix, not Emscripten, not WASI.

Changed in version 3.6: Accepts a [path-like object](https://docs.python.org/3/glossary.html#term-path-like-object).
strong pagoda
#

^do that inside os.fork()

#
# main.py
import os,sys
somevar = someimportantdata
pid = os.fork()
if pid:
    # this is the parent process... do whatever needs to be done as the parent
else:
    # we are the child process... lets do that plugin thing!
    os.setgid(gid) # Important! Set GID first! See comments for details.
    os.setuid(uid)
    os.chroot(chrootpath)
    import untrustworthyplugin
    untrustworthyplugin.run(somevar)
    sys.exit(0)
wise phoenixBOT
#
Python help channel closed

This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.