-
Notifications
You must be signed in to change notification settings - Fork 370
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Hy builtins as actual builtins #1979
Conversation
This is really cool! I think it would make a lot of sense to follow up with #1644 after this. |
I'd say this isn't ideal since we're still effectively polluting namespaces; we're just doing in a different, sneakier way. But it's an improvement over the status quo given things like #791. Why not include the Hy model names in the builtins injection? Then you could still say How does this change #1978? You're only touching Python-level imports, not Hy's macro namespaces, right? |
That's also an option, I can update to do that if we're not going the #1644 route.
Investigating further, it doesn't — I got too excited. Looks like I was running into the compiled/not-compiled effect again. E.g. in the example in #1978, if |
I believe if I add a |
I think #1644 is going to happen as part of the bigger core cleanup, whereas this PR is a stopgap method to fix some bugs before we get that. Might as well not add some breaking changes before then. |
2d63356
to
afb452f
Compare
Ok, I've cleaned it up a bunch more. Hy models are now part of the core (and so don't need to be qualified), and the builtin macros now go into the Furthermore, the builtins injection is now something that happens when you import In other news, I spent several days working through various other methods to try and get the core accessible without bodging it into builtins, but most of them didn't work out for one reason or another. Other failed experiments, documented for posterity:
|
Don't forget new tests and NEWS items for the new fixes. Instead of providing
I don't understand this change. The new version looks to be equivalent, longer, and unrelated to the other changes in this commit.
This looks like a leftover from earlier.
I think you can simplify this by making sure
These changes look like they were intended for an earlier commit.
This is an undocumented implementation detail and doesn't need to be in NEWS.
This is a bugfix, not a new feature, and it should be described in a way so that users unfamiliar with Hy's internals can tell what it means, like "Fixed a bug in which Hy model objects weren't always in scope". |
Can do!
I want
Can do. In fact, I don't think we need |
Huh, fair enough. I'm not sure I entirely understand how your change fixes that, but I think it might be enough to change
Cool, sounds good. |
Heads up, I noticed this in the Python 3.10.0a5 changelog, which might have consequences for this PR:
|
Oh that's interesting. I'll have to spin up a copy of 3.10 and test this then, make sure it still works. |
afb452f
to
43175b0
Compare
Finally back around to this with the requested changes. Playing around with 3.10 a bit it looks like Python's new |
Great work.
Instead of this, you can change
It's weird that you would need to
Why It looks like we neglected to update
|
43175b0
to
8d2f197
Compare
Thanks! I feel like I've learned... quite a lot about how Python imports and namespacing works.
The only other way I've found of doing this was the previous code that iterated through
Now that's just embarrassing, I clearly need to spend more time getting Hy. |
Heckin' weird. I'm going to give it a shot if only to convince myself that you're right. |
8d2f197
to
f27739d
Compare
The changes to macro handling broke |
This is really great work scauligi. I'm going to try and do a more comprehensive review tonight. Just wanted to pop in and mention that ;; hy.cmdline.HyREPL
loader = HyLoader("__hystartup__", os.environ.get("HYSTARTUP"))
mod = loader.load_module()
imports = mod.__dict__.get(
'__all__',
[name for name in mod.__dict__ if not name.startswith("_")]
)
imports = {name: mod.__dict__[name] for name in imports}
# Load imports and defs
self.locals.update(imports)
# load module macros
require(mod, self.module, assignments='ALL') This may be slightly more idiomatic than |
Looking through our options, I think it's probably best to loop over
|
I'm not sure this test is right. In the future, |
Would probably want to check that the item exists in Another thing i'm noticing is that if you start a regular python repl and import hy, it also imports everything into builtins which seems less than ideal. python-venv-3.9.2 ❯ python
Python 3.9.2 (default, Feb 20 2021, 00:00:00)
[GCC 10.2.1 20201125 (Red Hat 10.2.1-9)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> rest
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'rest' is not defined
>>> import hy
>>> rest
<function rest at 0x7f050447bdc0> |
That change would make the error silent, so it wouldn't be detected for a while, whereas not checking means a crash will occur as soon as you try to run Hy after messing up
Yeah, this is an inherent limitation. Remember that this is only a stopgap measure anyway. |
I'm not sure I follow? We definitely want If we change
Until we can figure out some better way of managing the namespace, I'm willing to treat this as a feature rather than a bug. After all, Hy is in some sense a language extension to Python.
Can do! |
Okay, I didn't realize such a bug existed. That makes more sense. You could add a comment like "check that an error is raised instead of documenting |
could we add an explicit call to import hy
hy._inject_builtins() |
That's how I originally had it, but this just delays the injection to when you import a hy module:
To me, it's less confusing to have the side effects happen from importing I'm increasingly of the opinion that infecting builtins is a feature and not a bug; however, if we still want to pursue keeping builtins clean, another avenue I can try is to replace all use of hy-core symbols like so:
On the plus side, I believe (without having tested it) that it should be able to cover any corner cases; on the minus side it's going to make spy/hy2py output (more) messy. |
Yeah, I'd recommend against that. I think it would be a bit of a mess. |
I may have found an option that makes it so we don't have to inject directly into python's builtins and polluting the python namespace with our core. From the python docs here
So modules pull from their own hy 0.20.0+78.ga8ccb7b3 using CPython(default) 3.9.2 on Linux
=> (import builtins)
=> (setv new-builtins {#** __builtins__})
=> (.pop new-builtins "zip")
<class 'zip'>
=> (setv __builtins__ new-builtins)
=> zip
Traceback (most recent call last):
File "stdin-f13e27693c85aed522df8c3fcb0bb0110ca54e14", line 1, in <module>
zip
NameError: name 'zip' is not defined
=> builtins.zip
<class 'zip'> |
I'm going to write a proposal for the core cleanup soon, which, once actually accomplished, will make most of this work obsolete, so maybe it's not worth a lot of effort. |
024db29
to
3ed4b50
Compare
Added the comment to the test for
This is one of the implementations I tried, unfortunately python (for whatever reason) doesn't respect the "local" # overwrite.py
__builtins__ = {"test": "it worked!"}
print(zip)
print(test)
Should we still merge in builtins injection for the time being? Or just drop it in favor of #1992's design? |
It's your choice. #1992 is going to take a while to happen and I probably would want to make a new release before a change that big (or at least, before some of those changes). If you want to go ahead with this, rebase it and I'll take a last look. |
3ed4b50
to
bf272ac
Compare
Might as well then, since it's already done the dirty work of ripping out autoimport insertion. |
Fixes #791, fixes #1045, fixes #1978.
I might have gotten a little carried away but I think I have a working implementation for getting rid of autoimports.
WIth this change, the compiler inserts a call to
hy.importer._hy_inject_builtins
at the top of each module. This function adds all of hy's core/stdlib to thebuiltins
module so that they can be called as if they had been imported.Upsides:
Possible downsides:
eval
tohy-eval
so it doesn't clash with python's builtin (alternatively, we could dropeval
from the core as mentioned in Don't include the namespace in the import's name for hy models. #1644 and have it used ashy.eval
instead)HySymbol
directly, but we can still usehy.HySymbol
)builtins
affects non-Hy code as well. E.g.:These will get shadowed by any local assignments though, so properly written python code will be unaffected. However, it's still a potentially confusing effect.