Decorate module imports with "module wrappers" similar to wrapper function/class, to create module templates

@decorator
import module

@decorator
from abc import d

In my understanding, modules (or python.py files) are like static classes in C# Where you can define functions variables, classes and so on, and you cant create instances from it.

When ever I want to use a static class, i would do

class StaticClass:
     propA = "value"
     propB = "value"
     ...
     @staticmethod
     def funA(cls): pass
     
     @staticmethod
     def funB(cls): pass

but i find it unhandy to write all this “@staticmethod” and having to reference cls as parameter

so i move it outside the class to the module

     
propA = "value"
propB = "value"

def funA():
    pass
def funB(): 
    global propA, probB
    pass

the only draw bag seems to be when editing variables of the module i need to use the global keyword.

in c# you can create Template Classes, and all this idea is about to being able to create kindof module Templates / in my understanding templated static classes with out the boilerplate @staticmethod def a(cls): … stuff

Lets say i have a module with many functions which i want to decorate with the same decorator.
Those decorators take a parameter v. We could mark the variable v as Template variable by defining a new Type

class Template(Any): pass

# module.py
v:Template = "somevalue"
@x(v)
def a():...

@x(v)
def b():...

...

Now i want to import the module multiple times but i want the parameter v to be another each time.

We could define a wrapper function that takes a module as argument and modify the module and return it.

def module_decorator(globals,vars,...): #the parameters are what ever properties a module can have

or we create a new Type for Modules where those parameters are stored

def module_decorator(module:Module,otherparams,*,v):
    copied_module = module.copy()
    #modify module ...

    copied_module.vars["v"].set(v) # set the v parameter of module.py
    copied_module.vars.filterByType(Template)["v"].set(v) # set the v parameter of module.py

    # we could also decorate all functions with some wrapper function  at this point
so i dont have to decorate all the functions seperately with @x(v)

    # copied_module.functions
    # copied_module.classes
    # copied_module.imported_modules
    
    return copied_module

So now we could easly do


@module_decorator(v="A")
import module as versionA


@module_decorator(v="B")
import module as versionB

...

In my usecase i could use this to create a module which implements the same sql queries but on different sql tables, and i pass the table name to the template module.

the only draw bag would be that when looking at the module it self, you cant know if its used as template.

this could lead to confusion, but i believe it could be benifitial anyways.

Self criticism:
I might not be the first one having this idea, and i may have misunderstood the concept of modules. Maby its bad practice to use a module as c# static class and wanting to template them is not a pythonic thing.

Anyways, since i didnt find anything about this topic so far id like to start a discussion about this and maby learn from it.

This seems like non-starter. Modules in Python are cached, there are not two copies made when you do “as” imports, and the “as” names are only local aliases to whatever one copy is in the cache.

In [1]: import sys

In [2]: import json as ja

In [3]: import json as jb

In [4]: "json" in sys.modules
Out[4]: True

In [5]: ja is jb
Out[5]: True

In [6]: "ja" in sys.modules
Out[6]: False

In your example above, module would get updated, and then have those changes overwritten when it gets updated again.

Changing module caching behavior would break some bedrock assumptions and cause problems for any package that does not expect module code to actually be repeatedly executed on re-import.

i may have chosen the wrong words,

the post above describes a behaviour i’d like to have in python.

my idea is to create new module “instances”/modules, with this decorator
and being able to modify them

import sys

@new_feature_decorator
import json as ja

import json as jb

"json" in sys.modules # out: True

"ja" in sys.modules # out: True

ja is json # False

jb is json # True

ja is jb # False


I understood (this is the “Ideas” category). I am saying that (IMO) this would be a drastic change to the language, and therefore, unlikely.

This would be extremely surprising behaviour. Normally, “as NAME” has no effect on the thing being assigned; and normally a decorator has a very specific meaning: calling a function on a thing after it’s been constructed and before it’s assigned. You MAY be able to get some support for import decorators with more conventional semantics, such as:

@new_feature_decorator
import json as ja

# roughly equivalent to
import json as ja
ja = new_feature_decorator(ja)

but I’m not sure whether this would fit your use-case.

TBH this seems like the wrong tool for the job. Python’s import system is designed around caching and abstract lookups. If you don’t want that, perhaps it would be better to instead build your design around directly opening and exec’ing the Python code; this also makes it pretty straight-forward to apply whatever changes you like.

Okay, then i have a few adjustments.

The idea is not attacking the statement: “import module as alias” does not copy the module.

It should only deep copy the module when being decorated. We could force the developer to use the keyword “as” to specify a new module $name, but there cant already exist an module with the name $name in the current directory

moduleA.py

a="a"
@dec
import moduleA as moduleB

is equivalent to

  1. creating a new file named “moduleB.py” in the same directory
  2. copy the content of moduleA in moduleB
  3. apply the changes defined in @dec
  4. import .moduleB

Answers to the above posted replies.
Ok now i understand the problem. Changing behavior of well defined keywords (“as”) in specific contextes (when import is being decorated) isnt best way to design a language.

I’m convinced that you can already achieve what you want to do using a custom import hook without requiring any changes to Python’s syntax. Roughly speaking, when you import a module:

  • The source code is located and read
  • It is executed
  • The resulting object is given a name and stored in sys.modules

(Have a look at A deep dive — ideas 0.0.38 documentation) for more details.

You could define an import hook that would give a special treatment to some module names, modifying them according to some rules that you specified, before executing them and storing them under a different name. I have not done an example like this, where the name of the module is changed every time a custom import hook is done and some custom rule is met, but I am fairly confident that it could be done.

1 Like