Converting import statements to absolute in PyCharm

  • #1
830
45

Summary:

I have this project in PyCharm that I have been trying to execute from the Python terminal. The problem is that the module I am using imports from other folders with import statements that act as though those modules reside in the root folder.

i.e.
I have many modules to import: [/folder/module1.py],...,[/folder/module105.py]
The blank __init__.py is assumed to be in /folder.
module: [/main.py] imports the former with the statement: [import module1],...,[import module105]
I sort of messed up while writing all my code, and now I regret it. Is there any way to fix all my import statements without manually going through every single one of the modules I created and appending the folder names to the start of each and every imported module name? I am unable to find any answers online, to my chagrin.
 

Answers and Replies

  • #2
12,031
5,693
Not understanding what you actually did to mess things up, there's a tool called fiximports that can reorganize them:

https://pypi.org/project/fiximports/

I've never used it before so you're on your own. As with any new tool usage, I would recommend backing up what you have before you use it on your files.

It's likely that what you've done can only be fixed by manually undoing it with the help of PyCharm and you'll have to decide that.

Were you attempting to make your program load faster? A major complaint with python is that it loads way more than it uses and that its hard to reduce the load without a lot of extracting and reorganizing stuff some of which you can't because its in someone else's module.
 
  • #3
830
45
Were you attempting to make your program load faster?
Actually, I just was not knowledgeable on the syntax of importing modules in Python. PyCharm just allowed me to import stuff from my modules as if they were all in the root folder. Thanks for the suggestion, besides. I'll look at it later and see if it is what I am looking for. I'll keep looking for other solutions, besides.
 
  • #4
pbuk
Science Advisor
Gold Member
1,636
549
Is there any way to fix all my import statements without manually going through every single one of the modules I created and appending the folder names to the start of each and every imported module name?
You could write a program to do it all for you. Python would be the ideal language :biggrin:
 
  • Like
Likes jedishrfu
  • #5
12,031
5,693
I see I'm not the only to write programs that write and modify programs. It must be a rite of passage for programmer programmers.
 
  • Like
Likes pbuk
  • #6
34,016
5,670
I see I'm not the only to write programs that write and modify programs.
But can you write a program that modifies itself? :oldbiggrin:
 
  • #7
12,031
5,693
Yes, I did that with the TEX programming language once. TEX allowed a program to write a file and to then execute it as code. TEX was based on a line editor command set with added variables, and control constructs created by Honeywell in the mid 1970s.

I was trying to develop a text game at the time and thought that would be pretty cool to change the rules of the game dynamically as the player played the game but then got more interested in intricacies of the task and not the game.

https://en.wikipedia.org/wiki/Text_Executive_Programming_Language

The example code in the TEX article (as was the article itself) was written by me when I was an Explorer Scout mentor:

Notable features[edit]
The most notable feature in TEX was its SUBS mode allowing variable values to crossover and become executable code. It allowed a programmer to create new variables on the fly to be used in later TEX expressions in a LISP-like fashion. TEX also allowed programmers to create scripts on the fly via line editing, saving the content to file later to be executed as part of the current program using interfile call and goto statements. However, in most cases, these features were used to provide simple dynamic goto statements in code as seen in the Adventure game parser example. What other kinds of Artificial Intelligence constructs could be developed were never fully explored.

An example of creating variables on the fly and then using them to do an interfile goto.
Code:
    _ incorporate x,y,z into the global variable pool
cmd="x=1 y=2 z=3"
subs ?
?cmd?

_ next we modify mycat/mypgm_1_2 to say "hello world" we are writing some code to execute later in our script
old mycat/mypgm_1_2
r:*cl:/!label_3 out:'Hello World'/
resave mycat/mypgm_1_2

_ lastly we subs in x,y,z and then evaluate the goto mypgm_1_2!label_3 which does an interfile goto
goto mycat/mypgm_?x?_?y?!label_?z?
The TEX program above illustrates dynamic script creation and then execution via substitution, file editing and interfile gotos. In effect, programs writing programs were possible although seldom done. In the above example, the mycat/mypgm_1_2 file would be executed at label_3 printing out "hello world".
I really liked the language, Honeywell had developed an extensive test suite for their timesharing commands in it and I was tasked with adding some of the GE extensions to the mix. In the end though GE mgmt never really required it to verify system operation from release to release.
 
  • #8
830
45
Okay, this ought to do the trick.

Python:
from os import walk

source=input("source")
destination=input("destination")

#   Folders to exclude
excluded=('.idea','__pycache__','VIEW HISTORY','compiled')

name_to_prefix={}
search_in=[]

first=True

for stuff in walk(source):
    path=stuff[0]; files=stuff[-1]
    ls=path.split('\\')
    tail=ls[-1]
    if tail in excluded:
        continue
    tail+='.'
    midtail=ls[-2]+'.'
    if first:
        first=False
        item1=tail
        path1=path
    if midtail in name_to_prefix.values():
        if midtail!=item1:
            tail=midtail+tail
    for file in files:
        if file[-3:] == '.py':
            module=file[:-3]
            if module == tail[:-1]:
                continue
            search_in+=[(path,file)]
            if item1!=tail:
                tail=item1+tail
#           if tail == item1:
#               continue
#         Edited [2020-09-16 | 4:48 PM HST]
            name_to_prefix[module]=tail

old_to_new=[]

for key,val in name_to_prefix.items():
    old_to_new+=[('from '+key,'from '+val+key)]
  
def copy_replace(src,dest,strings=old_to_new):
    '''
    :param src: Path to source text file
    :param dest: Path to destination
    :param strings: list of 2-tuples
    :return: None
    '''
    original=tuple()
    with open(src,'r') as r_file:
        for line in r_file.readlines():
            original+=line,

    new=tuple()
    for line in original:
        l=line
        for tpl in strings:
            l=l.replace(tpl[0],tpl[-1])
        new+=l,

    l=dest.split('\\')[:-1]
    s='\\'.join(l)
    from os.path import exists
    from os import mkdir
    if not exists(s):
        mkdir(s)
    with open(dest,'w') as w_file:
        for line in new:
            w_file.write(line)


def list_diff(biglist,smalllist):
    if len(biglist)<len(smalllist):
        biglist,smalllist=smalllist,biglist
    l=[]
    for m in biglist:
        if m not in smalllist:
            l+=[m]
    return l


def proper_import(src=source,dest=destination,files=search_in):
    '''
    :param dest: Where to save the stuff
    :param files: List of 2-tuples of str objects
    :return: None
    '''
    for path,file in files:
        l=dest.split('\\')
        s=src.split('\\')
        p=path.split('\\')
        branch=list_diff(p,s)
        if len(branch) > 0:
            l='\\'.join(l+branch)
        else:
            l='\\'.join(l)
        copy_replace(src=path+'\\'+file,dest=l+'\\'+file)

if __name__=='__main__':
    proper_import()
 
Last edited:
  • #9
12,031
5,693
Looks good but only testing will tell. Also its written exclusively for windows OS with the "\" path separators everywhere. Consider using os.path.sep:

Python:
# expect to see a / on Linux and MacOS and a \ on Windows
print(path.sep)

# alternatively there are functions that handle it for you in os.path
path.join(path.sep,'xxx','yyy','zzz')    # returns /xxx/yyy/zzz
path.split('/xxx/yyy/zzz')               # returns('xxx', 'yyy', 'zzz')
The best written programs of keyboard and mouse often go awry.
 
Last edited:
  • #10
830
45
Aye. Duly noted. This code should fix all 'from ... import ...' import statements for user-defined modules. I personally never used the 'import' format.

fixmy:
from os import walk
from os.path import sep

#    The path where "paths.txt" is located, including "paths.txt"
#    First line should contain path to source, second should have path to destination
filename=''

k=0

with open(filename,'r') as r_file:
    for line in r_file:
        if k >1:
            break
        line=line.strip()
        if k==0:
            source=line
        else:
            destination=line
        k+=1

oldname=source.split(sep)[-1]
newname=destination.split(sep)[-1]

#   Folders to exclude
excluded=('.idea','__pycache__','VIEW HISTORY','compiled')

name_to_prefix={}
search_in=[]

first=True

for stuff in walk(source):
    path=stuff[0]; files=stuff[-1]
    ls=path.split(sep)
    tail=ls[-1]
    if tail in excluded:
        continue
    tail+='.'
    midtail=ls[-2]+'.'
    if first:
        first=False
        item1=tail
    if midtail in name_to_prefix.values():
        if midtail!=item1:
            tail=midtail+tail
    for file in files:
        if file[-3:] == '.py':
            module=file[:-3]
            if module == tail[:-1]:
                continue
            search_in+=[(path,file)]
            name_to_prefix[module]=tail

old_to_new=[]

for key,val in name_to_prefix.items():
    repl_val='from '+ val + key
    if oldname == repl_val[5:5+len(oldname)] or ' '+oldname in repl_val:
        repl_val=repl_val.replace(oldname,newname)
    else:
        u=repl_val.split(' ')
        u.insert(1,newname+'.')
        mod=[u[1]+u[2]]
        repl_val=' '.join([u[0]]+mod)
    n = len(newname)
    old_to_new += [('from ' + key, repl_val),('from '+oldname+repl_val[5+n:],repl_val)]

def copy_replace(src,dest,strings=old_to_new,replace=True):
    '''

    :param src: Path to source text file
    :param dest: Path to destination
    :param strings: list of 2-tuples
    :param replace: Indicates whether or not to replace
    :return: None
    '''
    original=tuple()
    with open(src,'r') as r_file:
        for line in r_file.readlines():
            original+=line,

    new=tuple()
    for line in original:
        l=line
        for tpl in strings:
            l=l.replace(tpl[0],tpl[-1])
        new+=l,

    l=dest.split(sep)[:-1]
    s=sep.join(l)
    from os.path import exists
    from os import mkdir

    ls=original
    if replace:
        ls=new

    if not exists(s):
        mkdir(s)
    with open(dest,'w') as w_file:
        for line in ls:
            w_file.write(line)


def list_diff(biglist,smalllist):
    l=[]
    for m in biglist:
        if m not in smalllist:
            l+=[m]
    return l


def fix_imports(src=source,dest=destination,files=search_in,replace=True):
    '''
    :param src: Where the module is being copied from
    :param dest: Where the module is being copied to
    :param files: List of 2-tuples of str objects
    :param replace: Indicates whether you wish to replace import names
    :return: None
    '''
    for path,file in files:
        l=dest.split(sep)
        s=src.split(sep)
        p=path.split(sep)
        branch=list_diff(p,s)
        if len(branch) > 0:
            l=sep.join(l+branch)
        else:
            l=sep.join(l)
        copy_replace(src=path+sep+file,dest=l+sep+file,replace=replace)


if __name__=='__main__':
    fix_imports(replace=True)
 

Related Threads on Converting import statements to absolute in PyCharm

Replies
4
Views
5K
Replies
4
Views
13K
Replies
14
Views
2K
  • Last Post
Replies
1
Views
6K
Replies
3
Views
10K
Replies
9
Views
1K
Replies
1
Views
11K
  • Last Post
Replies
3
Views
1K
Replies
2
Views
482
Top