Skip to content

Commit 0b172a4

Browse files
committed
clarify the script entry point.
1 parent 4ea13ad commit 0b172a4

File tree

2 files changed

+50
-32
lines changed

2 files changed

+50
-32
lines changed

source/exercises/mailroom/mailroom-pkg.rst

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,25 @@ Code Structure
1010

1111
Start with your existing version of mailroom.
1212

13-
It should already be structured with the "logic" code distinct from the user interface (yes, a command line *is* a user interface). But you may have it all in one file. This isn't *too* bad for such a small program, but as a program grows, you really want to keep things separate, in a well organized package.
13+
It may already be structured with the "logic" code distinct from the user interface (yes, a command line *is* a user interface). But you may have it all in one file. This isn't *too* bad for such a small program, but as a program grows, you really want to keep things separate, in a well organized package.
1414

1515
The first step is to re-structure your code into separate files:
1616

17-
- one (or more) for the logic code: the code than manipulates the data
18-
- one for the user-interface code: the code with the interactive loops and all the "input" and "print" statements
19-
- one (or more) for tests.
17+
- One (or more) for the logic code: the code that manipulates the data
18+
- One for the user-interface code: the code with the interactive loops and all the "input" and "print" statements
19+
- One (or more) for the unit tests.
2020

2121
You should have all this pretty distinct after having refactored for the unit testing. If not, this is a good time to do it!
2222

23-
In addition to those three, you will want to write a top-level script file (perhaps called ``mailman.py``) that does little but import the ui code and run a ``main()`` function. It should look something like this:
23+
In addition to those three, you will need a single function to call that will start the program.
24+
That can be defined in a new file, as a "script", but for something as simple as this, it can be in with your interface code.
25+
That file can then have an ``if __name__ == "__main__"`` block
26+
which should be as simple as:
2427

2528
.. code-block:: python
2629
27-
#!/usr/bin/env python
28-
from mailman import cli
29-
3030
if __name__ == "__main__":
31-
cli.main()
32-
33-
Yes, that's it! This has the advantage of keeping the top-level script really simple, as it has to get put somewhere else and it can keep the "real" code in the package where it belongs.
34-
35-
.. note:: Be careful here -- it is important not to call your top-level script the same thing as your package, in this case ``mailroom.py``. If you do, than when installed, python will find the script, rather than the package, when you do ``import mailroom``. You can call it ``mailroom`` without the Python, but that may confuse Windows.
31+
main()
3632
3733
3834
Making the Package
@@ -51,27 +47,53 @@ Put all these in a python package structure, something like this::
5147
test_model.py
5248
test_cli.py
5349

54-
You will need to import the logic code from model.py in the cli code in order to use it. You can wait until you learn about mocking to write the code in test_cli.py (so you can leave that out)
50+
You will need to import the logic code from model.py in the cli code in order to use it.
51+
You can wait until you learn about mocking to write the code in test_cli.py (so you can leave that out)
5552

56-
Now write your ``setup.py`` to support your package.
53+
Now write a ``setup.py`` file to support the installation of your package.
5754

5855

5956
Making the top-level script runnable
6057
------------------------------------
6158

6259
To get the script installed you have two options. I used to prefer the more straightforward one, `the scripts keyword argument <http://python-packaging.readthedocs.io/en/latest/command-line-scripts.html#the-scripts-keyword-argument>`_
6360

64-
But it turns out that while the simple ``scripts`` keyword argument method works well and is simple, it may not work as well on Windows -- it relies in your script being named ``something.py`` and that Windows is configured to run all files with ``.py`` extensions. Not all windows systems are set up this way. But the "entry points" method builds a little exe file to call your script, so it's more reliable.
61+
But it turns out that while the simple ``scripts`` keyword argument method works well and is simple, it may not work as well on Windows -- it relies in your script being named ``something.py`` and that Windows is configured to run all files with ``.py`` extensions. Not all windows systems are set up this way. But the "entry points" method builds a little ``.exe`` file to call your script, so it's more reliable.
6562

66-
And the Python community has moved very much towards using setuptools entry points, so That's really the way to go now:
63+
And the Python community has moved very much towards using setuptools entry points, so that's really the way to go now:
6764

6865
http://python-packaging.readthedocs.io/en/latest/command-line-scripts.html#the-console-scripts-entry-point
6966

67+
In this case, that will look a lot like this:
68+
69+
.. code-block:: python
70+
71+
entry_points={'console_scripts': ['mailroom=mailroom.cli:main']},
72+
73+
That's a bit complicated, so I'll break it down for you. In this case, we only want a single script, but setuptools allows multiple types of entry points, and multiple instances of each type (e.g. you can have more than one script) to the code, so the argument is a potentially large dictionary.
74+
The keys of the dict are the various types of entry points.
75+
In this case, we want a single script that can be run in a terminal (or "console"), so we have a dict with one key: ``console_scripts``.
76+
77+
The value of that entry is a list of strings -- each one describing the console script. This string is of the form::
78+
79+
SCRIPTNAME=MODULE:FUNCTION_NAME
80+
81+
setuptools will create a wrapper script with the name given, and that wrapper will call the function in the module that is specified.
82+
So: ``'mailroom=mailroom.cli:main'`` means: create a start up script called "mailroom" that will then call the ``main`` function in the ``cli`` module in the ``mailroom`` package.
83+
84+
Once this is all set up, and you install the package (either in editable mode or not)::
85+
86+
pip install -e ./
87+
88+
you should then be able to type "mailroom" at the command line and have your program run.
89+
7090

71-
Including data files
91+
Including Data Files
7292
--------------------
7393

74-
NOTE: If you have a database of donors in a file that you load, then that should go in the package as well. Probably inside the mailroom dir, in a ``data`` dir or similar. Then you need to add it to your setup.py to make sure it gets copied into the installed package.
94+
If you have a database of donors in a file that you load, then that should go in the package as well. Probably inside the mailroom dir, in a ``data`` dir or similar. Then you need to add it to your setup.py to make sure it gets copied into the installed package.
95+
96+
(If you are not saving the donor data to a file -- that's fine. You can ignore this section for now)
7597

7698
There are a few ways to do this:
7799

@@ -83,7 +105,10 @@ I personally like the simplest one with the least magic:
83105

84106
Then you'll get the data file included in the package in the same place.
85107

86-
Now you'll need to write your code to find that data file. You can do that by using the __file__ module attribute -- then the location of the data file will be relative to the __file__ that your code is in. A little massaging with a ``pathlib.Path`` should do it.
108+
Now you'll need to write your code to find that data file.
109+
You can do that by using the ``__file__`` module attribute, which is the path to a python module at run time -- then the location of the data file will be relative to the path that your code is in.
110+
A little massaging with a ``pathlib.Path`` should do it.
111+
87112

88113
Testing your Package
89114
--------------------
@@ -104,23 +129,15 @@ When that is done, you should be able to run the top-level script from anywhere:
104129

105130
.. code-block:: bash
106131
107-
$ mailroom.py
132+
$ mailroom
108133
109134
and run the test from within the package:
110135

111136
.. code-block:: bash
112137
113138
$ pytest --pyargs mailroom
114139
115-
If you installed in editable mode, then you can update the code and re-run the tests or the script, and it will use the new code right away.
116-
117-
118-
119-
120-
121-
122-
123-
124-
140+
(or run the tests from the test dir as well)
125141

142+
If you installed in editable mode, then you can update the code and re-run the tests or the script, and it will use the new code right away.
126143

source/exercises/python_pushups.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
Python Pushups
55
##############
66

7-
These are a couple exercises to kick you off with Python
7+
These are a quick exercise to kick you off with Python:
8+
89

910
Explore Errors
1011
==============

0 commit comments

Comments
 (0)