I see a lot of Python people trying to tackle the C-esque problem of forward declaration for type-hinting purposes. There is much following of the typing
module’s example, loading modules and names, ballooning globals(), just to be able to hint names from them, and lots of comment-hinting and other workarounds.
It feels especially wasteful in code that makes no use of runtime introspection.
Perhaps a Python solution to a Python problem might be better.
import def <non-wildcard import statement>;
from <path> import def <non-wildcard list>;
Other wordings might be viable, I chose import def
because it is import related and def
is a keyword
>>> import def
File "<stdin>", line 1
import def
and we’re only seeking to import the definition of things (forward definition, if you will).
This statement WOULD NOT impact on the globals() namespace, it WOULD NOT [immediately] attempt to load anything.
Any runtime side-effects WOULD BE deferred to the point where someone tries to perform runtime introspection.
Meanwhile, type-analyzers can use this to recognize where to obtain hinting information from.
Example:
# Current code
from typing import Union # for emphasis, aware it is deprecated.
from . Adaptors import Base
# None of these are actually used in this module.
from . Adaptors.Mesh import Mesh
from . Adaptors.Shader import Shader
from . Adaptors.Texture import Texture
# None of these are actually used in this module.
from Packages.Converters.ConvertMesh import MeshConverter as MeshC
from Packages.Converters.ConvertShader import ShaderConverter as ShaderC
from Packages.Converters.ConvertTexture import TextureConverter as TextureC
def process(asset: Union[Mesh, Shader, Texture], converter: Union[MeshC, ShaderC, TextureC]):
if Base.legal_conversion(asset, converter):
converter.convert(asset)
print(globals())
The globals printed here would include process, Union, Mesh, Shader, Texture, MeshC, ShaderC and TextureC
, despite being entirely unneccesary.
If this becomes the following, meaning none of the ‘import def’ symbols would be available in the module’s namespace.
from typing import def Union
from . Adaptors import Base # actual import
from . Adaptors.Mesh import def Mesh
from . Adaptors.Shader import def Shader
from . Adaptors.Texture import def Texture
from Packages.Converters.ConvertMesh import def MeshConverter as MeshC
from Packages.Converters.ConvertShader import def ShaderConverter as ShaderC
from Packages.Converters.ConvertTexture import def TextureConverter as TextureC
def process(asset: Union[Mesh, Shader, Texture], converter: Union[MeshC, ShaderC, TextureC]):
if Base.legal_conversion(asset, converter):
converter.convert(asset)
print(globals())
then the only non-default global would be ‘process’ - none of the others would appear in the namespace because they are purely type hinting definitions.
Open Questions:
- Can this be implemented to solve inter-module circular dependencies?
- Can we create declaration imports?
– Something similar to all in index? E.g. def?
– ‘from Module import def *’ to allow creation of forward-declaration ‘headers’? - How do I define an alias without a real import?
– How do you write Tree = Iterable[Tuple[Path, DirEntry]] without importing all of these?