Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compile Simple Python package #1185

Merged
merged 5 commits into from
Oct 20, 2022

Conversation

Thirumalai-Shaktivel
Copy link
Collaborator

Related: #992

@Thirumalai-Shaktivel Thirumalai-Shaktivel marked this pull request as draft October 7, 2022 13:05
test_a.py Outdated
Comment on lines 1 to 7
import a
from a import print_a

print(print_a())
print(a.print_a())
print(a.print_b())
print(a.print_c())
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above code works, Can anyone please review and provide feedback?

Copy link
Collaborator Author

@Thirumalai-Shaktivel Thirumalai-Shaktivel Oct 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the design in this PR is not correct
Consider the example
test_a.py

import a

ASR

(TranslationUnit 
    (SymbolTable
        1
        {
            a:
                (Module 
                    (SymbolTable
                        3
                        {
                            print_a:
                                (ExternalSymbol 
                                    3 
                                    print_a 
                                    5 print_a 
                                    b 
                                    [] 
                                    print_a 
                                    Public
                                ), 
                            print_b:
                                (ExternalSymbol 
                                    3 
                                    print_b 
                                    5 print_b 
                                    b 
                                    [] 
                                    print_b 
                                    Public
                                ), 
                            print_c:
                                (ExternalSymbol 
                                    3 
                                    print_c 
                                    9 print_c 
                                    c 
                                    [] 
                                    print_c 
                                    Public
                                )
                            
                        }) 
                    a 
                    [b
                    c] 
                    .false. 
                    .false.
                ), 
            b:
                (Module 
                    (SymbolTable
                        5
                        {
                            print_a:
                                (Function 
                                    (SymbolTable
                                        6
                                        {
                                            _lpython_return_variable:
                                                (Variable 
                                                    6 
                                                    _lpython_return_variable 
                                                    ReturnVar 
                                                    () 
                                                    () 
                                                    Default 
                                                    (Character 1 -2 () []) 
                                                    Source 
                                                    Public 
                                                    Required 
                                                    .false.
                                                )
                                            
                                        }) 
                                    print_a 
                                    [] 
                                    [(= 
                                        (Var 6 _lpython_return_variable) 
                                        (StringConstant 
                                            "A" 
                                            (Character 1 1 () [])
                                        ) 
                                        ()
                                    )
                                    (Return)] 
                                    (Var 6 _lpython_return_variable) 
                                    Source 
                                    Public 
                                    Implementation 
                                    () 
                                    .false. 
                                    .false. 
                                    .false. 
                                    .false. 
                                    .false. 
                                    [] 
                                    [] 
                                    .false.
                                ), 
                            print_b:
                                (Function 
                                    (SymbolTable
                                        7
                                        {
                                            _lpython_return_variable:
                                                (Variable 
                                                    7 
                                                    _lpython_return_variable 
                                                    ReturnVar 
                                                    () 
                                                    () 
                                                    Default 
                                                    (Character 1 -2 () []) 
                                                    Source 
                                                    Public 
                                                    Required 
                                                    .false.
                                                )
                                            
                                        }) 
                                    print_b 
                                    [] 
                                    [(= 
                                        (Var 7 _lpython_return_variable) 
                                        (StringConstant 
                                            "B" 
                                            (Character 1 1 () [])
                                        ) 
                                        ()
                                    )
                                    (Return)] 
                                    (Var 7 _lpython_return_variable) 
                                    Source 
                                    Public 
                                    Implementation 
                                    () 
                                    .false. 
                                    .false. 
                                    .false. 
                                    .false. 
                                    .false. 
                                    [] 
                                    [] 
                                    .false.
                                )
                            
                        }) 
                    b 
                    [] 
                    .false. 
                    .false.
                ), 
            c:
                (Module 
                    (SymbolTable
                        9
                        {
                            print_c:
                                (Function 
                                    (SymbolTable
                                        10
                                        {
                                            _lpython_return_variable:
                                                (Variable 
                                                    10 
                                                    _lpython_return_variable 
                                                    ReturnVar 
                                                    () 
                                                    () 
                                                    Default 
                                                    (Character 1 -2 () []) 
                                                    Source 
                                                    Public 
                                                    Required 
                                                    .false.
                                                )
                                            
                                        }) 
                                    print_c 
                                    [] 
                                    [(= 
                                        (Var 10 _lpython_return_variable) 
                                        (StringConstant 
                                            "C" 
                                            (Character 1 1 () [])
                                        ) 
                                        ()
                                    )
                                    (Return)] 
                                    (Var 10 _lpython_return_variable) 
                                    Source 
                                    Public 
                                    Implementation 
                                    () 
                                    .false. 
                                    .false. 
                                    .false. 
                                    .false. 
                                    .false. 
                                    [] 
                                    [] 
                                    .false.
                                )
                            
                        }) 
                    c 
                    [] 
                    .false. 
                    .false.
                ), 
            main_program:
                (Program 
                    (SymbolTable
                        11
                        {
                            
                        }) 
                    main_program 
                    [] 
                    []
                )
            
        }) 
    []
)

Instead of creating ExternalSymbol in the module a, we have to replace it with the actual Function, variables, ...
Also, we should not create the module b and c symtab.
Reason:
1.

from a import b

Here from a will import the module b, so import b throws b already defined (Solution: we have to skip the import b)
2.

import a
print(b.print_a())

The above code should throw an error. but it doesn't, as module a import module b also.

What are your thoughts? @certik @czgdp1807

@czgdp1807
Copy link
Collaborator

I think we first need to complete #1065. I have assigned @Shaikh-Ubaid to try completing it.

@certik
Copy link
Contributor

certik commented Oct 12, 2022

@czgdp1807 I think the #1065 is needed, but that is the next step, an optimization. I think at first all we need to do when we see any import statement is to see if the module is already loaded in ASR, and if so, use it; otherwise find the file on the disk and load it to ASR and use it. When compiling a program (application) that depends on 100 dependencies, this approach will work.

The #1065 is for the use case when you want to precompile a library ahead of time. I think the ASR (without optimizations) is platform independent, so a given library can be fully precompiled even into a single ASR binary file, and then when you need anything from the library, you just quickly load that file. We definitely want that, but we can simply slowly do this as an optimization.

@certik
Copy link
Contributor

certik commented Oct 12, 2022

This looks great as a start, so I suggest we wrap this PR up (it's not going to be perfect), and then we'll send more PRs to iteratively improve various aspects of this.

TODO before merging:

  • move the tests into integration_tests
  • polish everything, I think no new features need to be added, just the code needs to be maintainable enough

@czgdp1807
Copy link
Collaborator

I think at first all we need to do when we see any import statement is to see if the module is already loaded in ASR, and if so, use it; otherwise find the file on the disk and load it to ASR and use it

This step is auto-compiling. One extra thing which #1065 is doing is saving the auto-compiled output in a pyc file. Correct me if I am wrong, so you are saying auto-compile a module if needed and include in the ASR in the TranslationUnit's global scope. Please let me know.

@certik
Copy link
Contributor

certik commented Oct 12, 2022

Two features:

  • The bigger feature we'll have to implement (later!) is nesting modules. We need to design this well at the ASR level and then make it work. We'll work towards it iteratively. It seems that in Python each module is completely standalone, and thus can be represented at the top level (as we already do in ASR), but we need to put the nesting in the name, so a module in the path a/b/c/d/e.py would have a name a.b.c.d.e.

  • we need to figure out how to represent things like import a and from a.b.c.d import e where (e is a module). It seems that import a creates a variable a in a symbol table. We should either reuse ExternalSymbol (pointing to a module!) or introduce something like a ModuleSymbol.

@certik
Copy link
Contributor

certik commented Oct 12, 2022

@czgdp1807 I think so. I think we just need to get something working first, then improve iteratively. It seems the saving to .pyc files is not necessary for the first implementation, it is a performance improvement (that we obviously need long term), so we can work on the .pyc file compiling separately.

@czgdp1807
Copy link
Collaborator

Sounds good to me. Auto-compile can be enabled easily and then including the module in global scope should also be simple. (AFAICT). So this seems doable.

@Thirumalai-Shaktivel
Copy link
Collaborator Author

Thirumalai-Shaktivel commented Oct 18, 2022

This PR is ready to go in.
Known issues to fix:

  1. The following should not work:
$ lpython -I /home/thirumalai/Open_Source/lpython integration_tests/test_import_01.py 
A
B
C

Instead do:

$ lpython -I /home/thirumalai/Open_Source/lpython/integration_tests integration_tests/test_import_01.py
  1. The following doesn't work:
$ lpython -I /home/thirumalai/Open_Source/lpython/integration_tests test_import_01.py
$

@Thirumalai-Shaktivel
Copy link
Collaborator Author

Thirumalai-Shaktivel commented Oct 18, 2022

I got questions:

  1. When to use -I? This feature is not available in CPython (Users do https://stackoverflow.com/questions/67631/how-do-i-import-a-module-given-the-full-path to import packages.)
  2. is -I intended to do lpython -I /home/thirumalai/Open_Source/lpython/integration_tests test_import_01.py? If so, how do we test this in comparison with CPython?
    P.S. using this PR lpython integration_tests/test_import_01.py works. Here lpython just looks for packages using the parent_dir path (i.e., integration_tests)

@certik certik marked this pull request as ready for review October 18, 2022 16:58
@certik certik requested a review from czgdp1807 October 18, 2022 19:34
@@ -37,6 +37,7 @@ struct CompilerOptions {
bool implicit_typing = false;
bool implicit_interface = false;
std::string target = "";
std::string import_path = "";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will probably later be std::vector<std::string>, but for now that's fine.

#if !defined S_ISDIR
#define S_ISDIR(m) (((m) & _S_IFDIR) == _S_IFDIR)
#endif
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be moved to the .cpp file and later we'll move it to a dedicated file, since we want most of our C++ files to be platform agnostic.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I will move this to utils.cpp file.

Copy link
Contributor

@certik certik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in general this looks good, I left some comments. We can later simply pass CompilerOptions into python_ast_to_asr, to avoid the long argument list.

@czgdp1807 go ahead and review this as well.

@Thirumalai-Shaktivel Thirumalai-Shaktivel added the ready for review PRs that are ready for review label Oct 19, 2022
Copy link
Contributor

@certik certik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

@certik certik merged commit d70be71 into lcompilers:main Oct 20, 2022
@Thirumalai-Shaktivel Thirumalai-Shaktivel deleted the compile_packages branch October 22, 2022 06:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready for review PRs that are ready for review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants