Python Project Layout: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(42 intermediate revisions by the same user not shown)
Line 7: Line 7:
<font size=-1>
<font size=-1>
  .
  .
  ├─ .gitignore
  ├─[[#Add_.gitignore| .gitignore]]
  ├─ requirements.txt
  ├─ [[#Expand_requirements.txt|requirements.txt]]
  ├─ run
├─ [[Pyproject.toml#Overview|pyproject.toml]]
  ├─ [[#Add_the_run_script|run]]
├─ [[#initialize|initialize]]
  ├─ src
  ├─ src
  │  └─ mypackage
  │  └─ mypackage
  │      ├─  __init__.py
  │      ├─  [[Python_Language_Modularization#init_.py|__init__.py]]
  │      ├─  __main__.py  
  │      ├─  [[#Add_main_.py|__main__.py]]
│      ├─ mypackage-part-1.py
│      ├─ mypackage-part-2.py
│      ├─ ...
  │      └─ VERSION
  │      └─ VERSION
  ├─ tests
  ├─ tests
Line 19: Line 24:
  │      ├─   
  │      ├─   
  │           
  │           
  └─ venv <font color=teal># created automatically upon virtual environment initialization</font>
  ├─ .venv <font color=teal># created automatically upon virtual environment initialization</font>
      ├─ bin
│    ├─ bin
      ...
│    ...
│         
└─ <span id='dist'></span>dist <font color=teal># created automatically upon [[Publishing_a_Python_Distribution_Package_in_a_Repository#Create_the_Distribution_Archives|publishing]] the project</font>
    ├─ somepkg-0.1.0.tar.gz
    └─ somepkg-0.1.0-py3-none-any.whl
</font>
</font>


Start with an empty <code>requirements.txt</code> file, it can be expanded incrementally.
Start with an empty <code>requirements.txt</code> file, it can be expanded incrementally.
=Initialize the Virtual Environment=
=Initialize the Virtual Environment=
Initialize the virtual environment following the manual procedure described here:
Initialize the virtual environment following the manual procedure described here:
Line 30: Line 40:
Update <code>pip</code> and, if <code>requirements.txt</code> has declared dependencies, install them:
Update <code>pip</code> and, if <code>requirements.txt</code> has declared dependencies, install them:
{{Internal|Python_Virtual_Environment#Virtual_Environments_and_pip|Python Virtual Environment &#124; Virtual Environments and <tt>pip</tt>}}
{{Internal|Python_Virtual_Environment#Virtual_Environments_and_pip|Python Virtual Environment &#124; Virtual Environments and <tt>pip</tt>}}
=Add <tt>.gitignore</tt>=
<syntaxhighlight lang='text'>
venv/
__pycache__/
idea/
.idea/
</syntaxhighlight>
=Add <tt>__main__.py</tt>=
=Add <tt>__main__.py</tt>=
Add the initial <code>__main__.py</code>:
Add the initial <code>__main__.py</code>:
<font size=-1>
<font size=-1>
  .
  .
  └─ src
  ├─ src
    └─ mypackage
│  └─ mypackage
        └─ __main__.py  
│      └─ __main__.py  
...
</font>
</font>


Line 47: Line 66:


</syntaxhighlight>
</syntaxhighlight>
Also see: {{Internal|Python_Language_Modularization#main_.py|Modularization &#124; <tt>main_.py</tt>}}


=Add the run script=
=Add the run script=
<font size=-1>
.
├─ run
...
</font>
<syntaxhighlight lang='bash'>
<syntaxhighlight lang='bash'>
#!/usr/bin/env bash
#!/usr/bin/env bash
Line 68: Line 83:


=Expand <tt>requirements.txt</tt>=
=Expand <tt>requirements.txt</tt>=
 
Add your dependency to <code>requirements.txt</code>:
 
-------
 
=Setting a Manual Project=
 
A project set up this way will be compatible with [[PyCharm_Concepts#Overview|PyCharm]]. Also see: {{Internal|PyCharm_Operations#Create_a_Simple.2C_Standalone_Project|Simple PyCharm Project}}
 
Add the dependencies to <code>requirements.txt</code>. For more details, see: {{Internal|Pip#requirements.txt|requirements.txt}}
 
Initialize the virtual environment:
 
<font size=-1>
<font size=-1>
  cd ${PROJECT_HOME}
  # ...
python3 -m venv venv
  PyGithub == 1.58.2
  venv/bin/pip install -r ./requirements.txt
</font>
</font>


For more details on virtual environments, see: {{Internal|Python_Language#Virtual_Environment|Virtual Environment}}
and run:


==<tt>.gitignore</tt>==
<syntaxhighlight lang='bash'>
<syntaxhighlight lang='text'>
venv/bin/pip install -r requirements.txt
venv/
__pycache__/
idea/
</syntaxhighlight>
</syntaxhighlight>
Also see: {{Internal|Pip#requirements.txt_File|<tt>pip</tt> &#124; <tt>requirements.txt</tt>}}
==<span id='initialize'></span><tt>initialize</tt>: Initialization and Dependency Maintenance Script==
<syntaxhighlight lang='bash'>
#!/usr/bin/env bash
# shellcheck disable=SC2086


==Bash Wrapper==
python -m venv "$(dirname $0)/venv"
"$(dirname $0)/venv/bin/python" -m pip install --upgrade pip
"$(dirname $0)/venv/bin/pip" install -r requirements.txt


<font color=darkkhaki>TODO: reconcile with [[Calling_Python_from_bash#Running_a_Python_Program_with_a_Bash_Wrapper|Calling Python from bash &#124; Running_a_Python_Program_with_a_Bash_Wrapper]]</font>.
if ! trust_root.sh --help >/dev/null 2>&1; then
  cat 1>&2 <<EOF


<syntaxhighlight lang='bash'>
trust_root.sh not found on your system.
#!/usr/bin/env bash


function main() {
Install it with:
  manage_venv "$@"
  PYTHONPATH="$(dirname $0)/src"
  export PYTHONPATH
  "$(dirname $0)/venv/bin/python3" -m gpm "$@"
}


#
[...]
# If the virtual environment does not exist, create it based on requirements.txt.
EOF
# If it does exist, recreate if '--force-init' is present among options.
else
#
  trust_root.sh "$(dirname $0)/venv"
fi
</syntaxhighlight>
Alternative (needs refactoring):
<syntaxhighlight lang='bash'>
# If the virtual environment does not exist, create it based on requirements.txt. If it does exist, recreate if '--force-init' is present among options.
function manage_venv() {
function manage_venv() {
   force_init=false
   force_init=false
Line 133: Line 140:
   "$(dirname $0)/venv/bin/pip" install -r "$(dirname $0)/requirements.txt"
   "$(dirname $0)/venv/bin/pip" install -r "$(dirname $0)/requirements.txt"
}
}
</syntaxhighlight>
=Set the PyCharm Project=
A project set up this way will be compatible with [[PyCharm_Concepts#Overview|PyCharm]]. To complete the PyCharm setup:
* Set the Python interpreter. Use the interpreter from <code>venv/bin/python</code>.
* Designate the <code>src</code> as the source root directory: Right Click → Mark Directory as → Sources Root
* Designate the <code>tests</code> as the test sources root directory: Right Click → Mark Directory as → Test Sources Root
==Setup PyCharm Debugging==


main "$@"
To setup main script debugging:
</syntaxhighlight>
 
Edit Configurations  → The + sign  → Python
 
Name: "__main__.py arg1 arg2"
 
Script path: Click on the folder icon and navigate. The final result is similar to: <code>/Users/ovidiu/projects/pygithub/src/pygithub_experiment/__main__.py</code>.
 
Parameters: ...


=TODO=
Environment variables:


<syntaxhighlight lang='text'>
PYTHONUNBUFFERED=1;GITHUB_PAT=...;MY_ENV_VAR_1=val1
</syntaxhighlight>
=A Build-System Independent Format for Source Trees=
<font color=darkkhaki>Process this: PEP 517 – A build-system independent format for source trees https://peps.python.org/pep-0517/</font>
<font color=darkkhaki>Process this: PEP 517 – A build-system independent format for source trees https://peps.python.org/pep-0517/</font>

Latest revision as of 02:54, 6 April 2024

Internal

Overview

A typical Python project layout, which allows for code written in other programming languages as well, is similar to:

.
├─ .gitignore
├─ requirements.txt
├─ pyproject.toml
├─ run
├─ initialize
├─ src
│   └─ mypackage
│       ├─  __init__.py
│       ├─  __main__.py
│       ├─ mypackage-part-1.py
│       ├─ mypackage-part-2.py
│       ├─ ...
│       └─ VERSION
├─ tests
│   └─ mypackage
│       ├─  
│           
├─ .venv # created automatically upon virtual environment initialization
│    ├─ bin
│    ...
│           
└─ dist # created automatically upon publishing the project
    ├─ somepkg-0.1.0.tar.gz
    └─ somepkg-0.1.0-py3-none-any.whl

Start with an empty requirements.txt file, it can be expanded incrementally.

Initialize the Virtual Environment

Initialize the virtual environment following the manual procedure described here:

Python Virtual Environment | Virtual Environment Creation

Update pip and, if requirements.txt has declared dependencies, install them:

Python Virtual Environment | Virtual Environments and pip

Add .gitignore

venv/
__pycache__/
idea/
.idea/

Add __main__.py

Add the initial __main__.py:

.
├─ src
│   └─ mypackage
│       └─ __main__.py 
...

def main():
    print('.')


main()

Also see:

Modularization | main_.py

Add the run script

#!/usr/bin/env bash
# shellcheck disable=SC2086

PYTHONPATH="$(dirname $0)/src"
export PYTHONPATH
"$(dirname $0)/venv/bin/python" -m mypackage "$@"
chmod a+x ./run

Expand requirements.txt

Add your dependency to requirements.txt:

# ...
PyGithub == 1.58.2

and run:

venv/bin/pip install -r requirements.txt

Also see:

pip | requirements.txt

initialize: Initialization and Dependency Maintenance Script

#!/usr/bin/env bash
# shellcheck disable=SC2086

python -m venv "$(dirname $0)/venv"
"$(dirname $0)/venv/bin/python" -m pip install --upgrade pip
"$(dirname $0)/venv/bin/pip" install -r requirements.txt

if ! trust_root.sh --help >/dev/null 2>&1; then
  cat 1>&2 <<EOF

trust_root.sh not found on your system.

Install it with:

 [...]
EOF
else
  trust_root.sh "$(dirname $0)/venv"
fi

Alternative (needs refactoring):

# If the virtual environment does not exist, create it based on requirements.txt. If it does exist, recreate if '--force-init' is present among options.
function manage_venv() {
  force_init=false
  while [[ -n $1 ]]; do
    if [[ $1 == '--force-init' ]]; then
      force_init=true
    fi
    shift
  done
  if [[ -d $(dirname $0)/venv ]]; then
    if ${force_init}; then
      rm -rf "$(dirname $0)/venv"
    else
      return 0
    fi
  fi
  echo "initializing venv ..."
  python3 --version 1>/dev/null 2>&1 || { echo "python3 not in PATH" 1>&2; exit 1; }
  python3 -m venv "$(dirname $0)/venv"
  "$(dirname $0)/venv/bin/pip" install -r "$(dirname $0)/requirements.txt"
}

Set the PyCharm Project

A project set up this way will be compatible with PyCharm. To complete the PyCharm setup:

  • Set the Python interpreter. Use the interpreter from venv/bin/python.
  • Designate the src as the source root directory: Right Click → Mark Directory as → Sources Root
  • Designate the tests as the test sources root directory: Right Click → Mark Directory as → Test Sources Root

Setup PyCharm Debugging

To setup main script debugging:

Edit Configurations → The + sign → Python

Name: "__main__.py arg1 arg2"

Script path: Click on the folder icon and navigate. The final result is similar to: /Users/ovidiu/projects/pygithub/src/pygithub_experiment/__main__.py.

Parameters: ...

Environment variables:

PYTHONUNBUFFERED=1;GITHUB_PAT=...;MY_ENV_VAR_1=val1

A Build-System Independent Format for Source Trees

Process this: PEP 517 – A build-system independent format for source trees https://peps.python.org/pep-0517/