The use of Python libraries is facilitated by a good Python program structure. The ix_all library
is a collection of all three libraries: ix_pi.py, ix_pico.py
and ix_py.py. Each of these libraries can be referenced individually. Source S155:08 describes
how to create an importable module. For me, the main advantage of importing one or
more modules is:
Imported modules (coded by me) are NOT displayed in the "code being debugged",
their being unchangeable can be a positive or negative feature.
In my opinion, the main limitations concerning Python file access and Python libraries of functions are:
libraries in higher-level folders cannot be accessed
members of libraries (e.g. loaded by import ix_py) need to be qualified
by a prefix when they are referenced in code
(use "as" name to get around this limitation)
libraries that are device-specific should be grouped together
libraries defined by "__init__" have conflicting non-distinct names
The following recommended code folder structure minimizes the above limitations:
NB Lines of code with a single entry point are called a procedure
NB Lines of code containg many objects (procedures & data) is called a module
NB a Python package is a collection of related objects that are bundled together
NB a file is a collection of data and/or code that may need special tools to handle
NB a Class is a collection of data and procedures that define programable objects
NB each Class (defined by code) has its own package
code that tests a library function should not clutter the libraries
the Python folder in Desktop holds all Python code as packages
the Python folder in Desktop holds no other folders
"__init__".py is avoided, modules in a folder will be used instead
functions packaged by being in a folder works in MicroPython
(although this still doesn't work in Python3 nor Python 3.7on the Pi)
"__init__".py is avoided, modules in package file also be used
most Python code is grouped into one or more packages (code groups)
each project library package name should be short and descriptive
functions should be grouped in packages that are device-specific eg pi, pico, py
program names should be computer-specific eg main_pico.py
(unless the name itself is computer-specific e.g. piButton.py
program names should contain a version number e.g. piButton_v02.py
(The version number can be chopped by renaming using "as" during import)
every Python code module should be in a package
utility programs should be grouped into one or more packages
A typical code structure (of packages) that follows these recommendations is:
For more thoughts on IX Packages see Article 164, Article 174, Article 190 and Article 215.
Within the above folder structure, current testing should be done in the sandBox folder. To test a module, the
module is first copied into the sandBox folder, testing is done making use of code in one or more of the
"library" packages. When testing is complete, the revised or new code is copied into the relevant package "libraries".
Functions: Collections vs Libraries
The concept of a code module is similar to a mathematical function. A code module can be as simple as the
simplest function but it can be more powerful because
it can also include procedures for coding, naming, storing and calculating. A program can use a collection of
procedures that are all defined within the program. Often the collection of procedures used are made into
usable functions or callable procedures.
At their simplest, these collections are included in the same text file,
usually appearing before the main procedure that calls them.
But the procedures that are called can be kept on the storage device in the same folder
as the main procedure that calls them. Most programming languages can find them if
the called procedures are in the same folder.
But when a set of procedures are bundled together, they can be placed in a folder contained in the
folder that holds the main program. Many languages can find them in such "lower" folders. Some
languages require special packaging when the procedures are located further away. Some versions
of Python expects an "__init__.py" text file to accompany procedures that are called.
Most operating systems that offer the Python language provide access to "built-in" functions and to
collections of Python functions that other programmers have made available to the general public.
Often collections of functions are available across many computer models and different Operating
Systems. The more well-known collections are called "libraries" of functions.
To make a collection of functions successfully available to many users, some very strict rules must
be followed. If such rules were not in place, chaos would result and programmers would not
know how to correctly use the procedures in the various environments.
As of 2024BFeb12, the webMaster is creating Article 215 which contains and explains ALL functions
(and Python programs) that he has written for the Raspberry PI. Most are written in Python. All
functions are listed alphabetically including all known versions of each function and the code for
each version. The main project using each function is shown as is the uSD or SSD device where
the function has been used.
Creating an Importable Module (Only on your PC)
Source S155:08 is a tutorial that teaches you how to create a module package that can be imported into any Python program
anywhere on your computer. The import statement such as "from motivate import motivate_me" will import the packaged
module from a directory named "motivate" not from a file named "motivate". It will function as if it were being
imported like any published package (e.g. RPi.GPIO ). When local modules are imported from within the same
folder, a much simpler "packaging process" can be used.
When you are ready to publish your package on the web, you will need to choose where others will go to
find your package (e.g. GitHub or PyPi etc). Publishing your package in this manner is called sharing or distributing
your package. When you prepare your package for distribution, you will need to provide documentation and
contact information so that future users can communicate with you. Source S155:08 provides additional information
that will help you prepare your package for wide distribution. It is possible to upload your package in a
sandbox environment that is used mainly for teaching and learning.
Code should be properly prepared and packaged before it is made available to the widespread public. I was
able to follow the steps in Source S155:08 and properly generate the "ultra-simple library package" described therein.
Of course, the package that I generated was NOT uploadable for widespread distribution. My next step is
to package a few of the functions that I have written into a library so that I can use them on various computers
that I operate. "Dunders" refer to the "double underline characters shown below.
An Ultra-Simple Directory Structure (and definitions) For a Package
-project: my_project
-package: motivate
-module: me.py
-initialisation file:__init__.py
-test program: test.py
terms defined for the package:
__project__ = "motivate"
__version__ = "0.0.1"
__description__ = "a Python module to motivate you"
__packages__ = ["motivate"]
A special setup.py program must be used to prepare the "package" described above.
Additional Information can be added for the future user(s) of this package.
See Source S155:08 for a detailed tutorial on how to do this.
Typical Application System File Structure
Desktop/appSys/appA/
main.py
(or appAmain.py)
appAcheckUp.py
userModuleA.py
userModuleB.py
appAlibr.py (package library of app functions)
ix_py.py (package library of non-machine-specific python functions)
ix_pi.py (package library of pi functions)
ix_pico.py (package library of pico functions)
progs/
appAreports.py
appAviewLog.py
etc
files/
fileA.txt, fileB,txt
data/
fileX.txt, fileY.txt
log/
2021FJun12/
various logs
media/
images/
sounds/
videos/
help/
doc1.txt, doc2.txt
documentation/
file1.doc, file2.odt, img.jpg
backups/
date/
etc
#end of appA
Notes about Libraries
Note: A utility routine named "setup" is needed to pack a package library. . . . and another to unpack it
Note: After using an "import package" statement, the module must be prefixed by the package name to use
e.g. import ix_py; aStr= ix_py.inputWto(":",10) gives the user 10 seconds to type in some text
e.g. import ix_py.py; textStr=ix_py.f3(3.14159);print(textStr) prints out 3.142
e.g. import ix_py; textStr=ix_py.f3(3.14159);print(textStr) prints out 3.142
e.g. from ix_py import f3; textStr=f3(3.14159);print(textStr) prints out 3.142
e.g. import f3_v3 from ix_py as f3; textStr=f3(3.14159);print(textStr) prints out 3.142
Note: As seen above, after using a from... import ... statement, no package name prefix is needed
The deprecated PiIX.py library can be found in Source S155:01 Derivation of the ix_libraries: Library Name ix_pi.py: pi (the Raspberry pi computer) and ix: resembles DC
Note 1: ix_py has not YET been uploaded to GitHub (as of 2021FJun24) Note 2: MicroPython in Thonny operates almost identically to Python3. (as of 2021FJun24)
but MicroPython works on files, modules and packages in the Pi Pico (not in the Pi)
To use ix_pi.py, place it in the directory containing the "main" Python program which has an "import ix_py" statement. This
main program or any of its functions can then successfully reference one of the functions in the ix_pi library. Source 4 supposedly
contains "concepts for beginners" but I think they are for intermediate programmers.
ix_Py functions (Listed Alphbetically)
## Names of Functions in ix_all:
--- ----------------
audioHDMI()
commandOS()
f3()
inputWto()
listSho()
Detailed List of ix_all.py Python Functions for the Raspberry Pi
Each of the functions listed below is preceded by a box titled "selectCode xx" where xx is
a number showing the order in which they are listed. It will be possible to use the IXp
macro processor to create the item in the list. For example, the commandOS item is
stored in Article 215 as a finished macro named "selectCode_f3_macro". The author (D@CC)
plans to use f3 as an example using the IXp macro facility. Another macro appearing in Article 215
is "selectCode_commandOS_macro".
f3()
-- end of f3_py.py program in 01
Purpose: converts a number to a string with format -999999.999 for subsequent printing
Form: f3(inObj) -> text
Reason: eliminates useless digits to the right of the decimal
Summary: converts a number < 1 million to a string with 3 digits right of .)
Example: txtStr = f3(-743.48637954) # will produce " -743.486"
Warning 1: if parameter is > +999999 or < -999999 it retains some significant digits and an exponent
Warning 2: if parameter is < +.001 or > -.001 it retains some significant digits and an exponent
Error 2: if parameter is not type: int,float (incl scientific), str, bool. it returns "f3:Bad Type".
Note 1: does not fail if given a parameter of bad type
Requires:
Version: none
For: PiR2 Area Controller and Mak Pi ADC
Repository: ix_py.py Function Library
Author: D@CC ( David@ColeCanada.com )
Date Created : 2021BFeb21
Date Tested : na
Date Modified: 2021BFeb21
Code: f3() by D@CC (David at ColeCanada.com)
inputWto()
-- end of inputWto_py.py program in 02
Purpose: input text from keyboard With TimeOut
Form: inputWto(str,nSecs) -> txtStr, isOK
Reason: allows a Python program to not "hang" on input() if unattended
Summary: allows nSecs for input of text & Enter from KeyBoard
Example: txtStr, isOK = inputWto(":",5) #seconds waited
Warning 1: txtStr="" if nSecs is not of type int or str or if "" was input
Warning 2: isOK = False (if there is no response within nSecs)
(or True if Enter typed within nSecs)
Note 1: requires hitting the Enter key after text is typed in.
Note 2: returns 2 parameters (which bothers some programmers).
Note 3: even if "isOK" is not referenced later, the program will continue
. . . . . . . as if an empty string had been entered.
Requires:
Version: none
For: PiR2 Area Controller and MakPiADC
Repository: ix_py.py Function Library
Author: D@CC ( David@ColeCanada.com )
Date Created : 2021BFeb19
Date Tested : 2021BFeb19
Date Modified: 2021BFeb19
Source: PyMOTW.com/w/select by Doug Hellman
Code: inputWto() by D@CC (David at ColeCanada.com)
listSho()
-- end of listSho_py.py program in 03
Purpose: show class and value of a variable
Form: sho(strA,isP) -> valueVarA
Reason: during debugging to show info about a variable
Summary: verifies args, varA should be a str eg "varA", if isP: prints class & value of varA
Example: p=1; intC=234; sho("intC",p) will print 'intC < class "int"> 234'
Help: type sho("-h",p) for help
Warning 1: variable name must be a string, not the name
Warning 2: if isP is 0, nothing will be printed
Warning 3: 2nd parameter is required
Warning 4: import show doesn't work: see requires
Note 1: error checking prevents system errors.
Note 2: when debugged change "p=1" to "p=0"
Requires:
Version: none
For: PiR2 Area Controller and MakPiADC
Repository: ix_all.py Function Library
Author: D@CC ( David@ColeCanada.com )
Date Created : 2021FJun26
Date Tested : 2021FJun26
Date Modified: 2021FJun26
Source: www by eresonance on 2013JOct23
Code: show() by D@CC (David at ColeCanada.com)
commandOS()
-- end of commandOS.py program in 04
Purpose: execute an OS command and return results
Form: commandOS(strCmd) -> strResult
Reason: permit code to execute OS command and use results
Summary: executes an OS command, saves results, returns results
Example: strD=commandOS("dir") will return list of dir contents
Help: type commandOS("-h") for help
Warning 1: Parm1 must be a str containing an OS command
Note 1: error checking prevents system errors.
Requires:
from ix_all import commandOS
Python Verson: 3
Globals: none
External Functions:
Imports: none
Classes: none
Files: temp result file
Version: none
For: PiR2 Area Controller and MakPiADC
Repository: ix_all.py Function Library
Author: D@CC ( David@ColeCanada.com )
Date Created : 2021FJun26
Date Tested : 2021FJun26
Date Modified: 2021FJun26
Source: www na on 20xxZmmm##
Code: commandOS() by D@CC (David at ColeCanada.com)
Special Handling of "sho()"
My function named "sho()" is an invaluable function used during debugging. It is easy to type and provides quick info
about any variable in the program. Often, just knowing the class (type) and value of a variable will help immensely in
understanding what is going wrong in code that you are debugging. Before creating "sho", to see the value of a variable
named "reading", I would type the statement 'print("reading:",reading)'. To reduce the number of keystrokes, I created
and used the function named 'sho'. To elminate the need to type the name of the variable twice, I used the internal
python function 'eval'. Then I decided that 'sho' should also output the class (i.e. type) of the variable. I also found
it desirable for sho() to check for errors in the calling parameters. Then I discovered
that I wanted to be able to "enable" the sho() function (or not) during debugging. That is when I added the second
(boolean) parameter. If the second parameter is "True", sho() performs normally, if the second parameter is "False",
the error checking is still done, but 'sho()' is silent, unless an error is discovered. If the second parameter is
initialised at the top of the program (e.g. p=True or p=1), it can be turned on/off with a keystroke. In fact, all the sho()
statements can be left in place (but cluttering) the code for potential future debugging use. If a future user
discovers my "sho()' function, it can be called with a variable name of "-h" which helps by describing how to use
it. An example of using "sho" is 'sho("reading",p) which (if p=1 or True) might print out:
reading: <class> str "97.145"
This resulting "sho()" function seemed to meet all of my needs. Then I discovered some of its underlying flaws.
For example, sho() had to be defined in the code of the main python routine, not as an imported module.
Furthermore, sho() did not function if the "sho()" statements were being used to debug a function called by
any other function. It is not clear whether global variables and imported variables would work with sho().
The resulting errors are of the nebulous "Python namespace" type. My simple 'sho()' function was becoming
overly complicated.
Well, I decided to 'copy-n-paste' an ultra-simple version of sho() into code where-ever it was needed. But this
solution meant that I couldn't simply import 'sho()' itself. So I created an importable version of sho() called
listSho()
that simply prints the definition of 'sho() ' as printed data output by the program calling listSho(). Then the
definition of "sho()" could simply be cut-n-pasted from the output window into the code window of the Python
interpreter. Wow - what a ride!
Fortunately, most of the functions that I create don't suffer these type of complexities.
As a result, when debugging a short quick program, I sometimes just code the verbose 'print("abc:",abc)'.
An alternative is to type 'import listSho' then 'listSho(1)' then 'p=1', then I copy-n-paste the code into my
main "test" program. But to do this, listSho.py needs to be discoverable by Python. So I often need to
find the module listSho.py and include it in the 'sandbox' folder containing my test program. Ugh!!
Where does Python3 look for libraries on a Raspberry Pi ?
Of course, Python expects to find "import" modules in the same directory (i.e. folder) where the main "entry point script" is located. This precludes the identification of a folder in the "import" statement. But Python also looks for "import" modules in some other "sys" locations.
The following commands (from Source S155:02) help you discover these other "sys" locations:
pi@raspberrypi:~ $ python3
Python 3.7.3 (default, Jul 25 2020, 13:03:44)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/usr/lib/python37.zip', '/usr/lib/python3.7', '/usr/lib/python3.7/lib-dynload', '/home/pi/.local/lib/python3.7/site-packages', '/usr/local/lib/python3.7/dist-packages', '/usr/lib/python3/dist-packages']
>>> exit()
pi@raspberrypi:~ $ cd /usr/lib/python3/
pi@raspberrypi:/usr/lib/python3 $ dir
dist-packages
pi@raspberrypi:~ $ dir dist-packages
******* a list of all the python3 files (i.e. libraries) will appear ***********
******* it includes "thonny" and "picamera" and hundreds more ***********
******* So I will place ix_py.py in this directory. ***********
******* Hopefully import ix_py.py will then work like any other library ***********
pi@raspberrypi:~ $exit
Creating a Help file for an import module
The Contents of a "well-made" help file are:
NAME
DESCRIPTION
CLASSES
FUNCTIONS
DATA
FILE
Within any function eg f3(), a simple Help file for the function f3 is defined by the statement below:
f3.__doc__ = "DESCRIPTION: A simple function to convert a number into a string with format -999999.999 ."
or
f3.__doc__= "doc f3.txt is in Desktop/IX_assets/ix/ix_pkg.txt"
This statement can often be many lines long, the statement can also contain LFs (LineFeeds). More info is in Source S15:01 and Source S155:03.
To view the f3.__doc__ in Python REPL, after importing the f3 module, type:
>>>print(f3.__doc__)
doc f3.txt is in Desktop/IX_assets/ix/ix_pkg.txt
Of course, more documentation of f3() should be stored in file f3.txt in the ix_pkg.txt package.