LibCST icon indicating copy to clipboard operation
LibCST copied to clipboard

Clarifying expression context of import statements

Open MapleCCC opened this issue 2 years ago • 3 comments

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? 🤔

MapleCCC avatar Aug 29 '22 15:08 MapleCCC

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

zsol avatar Aug 29 '22 15:08 zsol

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.)

MapleCCC avatar Aug 29 '22 15:08 MapleCCC

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

zsol avatar Sep 14 '22 14:09 zsol