LibCST
LibCST copied to clipboard
Clarifying expression context of import statements
Currently ExpressionContextProvider
treats import statement as having LOAD
context. For a tiny example as import a
:
import libcst as cst
from libcst.metadata import ExpressionContextProvider
from libcst.helpers import ensure_type
module = cst.parse_module("import a")
wrapper = cst.MetadataWrapper(module)
metadata = wrapper.resolve(ExpressionContextProvider)
line = ensure_type(wrapper.module.body[0], cst.SimpleStatementLine)
a = ensure_type(line.body[0], cst.Import).names[0].name
print(metadata[a]) # result is ExpressionContext.LOAD
This may look correct and natural at first sight. But if we think more carefully, we see that the symbol a
in the module namespace is actually being STORE
'ed. (we can think of a symbol like a designation pointing to a tiny storage unit within a large storage room called module namespace)
The import statement is actually loading somethingᵀᴹ from some external place, and store this somethingᵀᴹ inside the symbol a
in the module namespace.
It's not retrieving something already previously stored inside symbol a
in the module namespace. Before the import statement, the module namespace stores nothing for symbol a
.
Based on these observations, I would prefer this being a STORE
context more than a LOAD
context. I could be missing something though. What's your thoughts on this? 🤔
Yeah I agree with this sentiment, the same way as a
is STORE
in a += 1
. Moreover, in this snippet:
import a.b as c
import d.e
from f.g import h as i
from f.g import j
We'd have these names and their contexts:
symbol name | expression context |
---|---|
a , b |
LOAD |
c |
STORE |
d , e |
STORE |
f , g , h |
LOAD |
i , j |
STORE |
Thanks for the swift reply. I am not 100% sure about treating X
in from X import Y
as a LOAD
though. There doesn't actually exist a symbol called X
in the module namespace.
Currently every kind of nodes which ExpressionContextProvider
provides a ExpressionContext
for always relates with some symbol in the namespace. If we treat X
as having a ExpressionContext
, we will be making an exception case here.
But I guess it ultimately depends on whether we really want to interpret the concept of "expression context" as related to symbol in namespace. I will wait for you to make the final decision here.
(For the information, the standard library ast
doesn't provide expression context for import statement at all. I don't know if it's intended or an overlook from their side.)
But I guess it ultimately depends on whether we really want to interpret the concept of "expression context" as related to symbol in namespace. I will wait for you to make the final decision here.
I've gone back and forth on this in the past two weeks without reaching a definite interpretation. I think I'm fine with treating ExpressionContext
as related to the symbol namespace as the current file is being interpreted by Python, mostly because it's simple and it matches the original intention of ExpressionContext
in the ast
module. In LibCST we'll be slightly extending it to apply more consistently (i.e. for imports)
So that means in the above table we don't need the rows containing a
, b
, f
, g
, h