209 IX: IX Family of Software: requires() Phase I (209.html)

Keywords

ICH180RR requires ISO9003 import prepIX_I.py show.py "version control" v ixv "function programmer" "user programmer" _v03 isShow "IX: PrepIX" ISO9003_Software_Version_Log.txt "LIST OF REQUIRED IX FUNCTION VERSIONS" Functions Macros Entities "Repositories of Functions" OOP Object-Oriented-Programs object module "Source code" prepIX_I "main program" Cytron "CM4 Maker board" "Compute Module 4" CM4 CM4008016 Repository CM4-IO-BASE-A &ix| nShowDefs showIt showIt_ix.py prepIn.py lexicon show prepIX_I "CR1220 button cell" ix_isShow FRUN(deprecatd) IXp IXb python Thonny Micropython CircuitPython CPython pkg_py.py mpkg_py.py apkg_py.py spkg_py.py *_batch.py piping main_py.py "function pairs" requires .py? sys.path.append textpack hashtotals "a versionned function" FRUM

/KeywordsEnd

(To enlarge .....Click it)
thumb: IXimage.jpg
IX or (IX by DC) or "|><"


This article is a part of the IX family of software.

Introduction

The "requires" function is an integral part of the IX Software Family. More is said about the whole IX Software Family in Source 1 below.

	The requires() function in the IX family is a very powerful component.  Due
	to its complexity, it is being implemented in 2 Phases. This article describes 
	Phase I of the requires(func(. . . . )) as implemented by prepIX_I in the IX family.
	The deprecated program prepIX is being replaced by prepIX_I.

	The prepIX_I program has not yet been fully coded as of 2023KNov025 The latest article 
	describing an early version of prepIX is article 196 (Source 10) dated 2023 E May 16.  
	Watch this article (209) for the release of prepIX_I which will include phase I of 
	"requires".
	
After describing the "requires" function of prepIX, the difference between functions, entities and macros will be explained. These related concepts are important in the IX Family of Software.

A useful IX function for debugging, named show() [version 0] and a useful IX macro definition named showIt_ix.py will both be fully explained and used as an example in this article.

Finally, repositories will be introduced. Repositories are locations (such as libraries, folders and packages) where functions (and other Python modules) can be found; within the source code of a program or stored, internally in a computer or externally on the web. Python repositories are endless!

If the reader encounters words or phrases that need to be explained, refer to the "lexicon" in article 196 (Source 10).

The "requires()" function (in prepIX_I)

The "requires" function is a very powerful extension of the standard "import" statement that is provided by Python. Requires will be introduced in 2 phases. In Phase I, the improved features of requires (over import) are:

	1) "requires" adds a function to a main program by prepending visible numbered statements.
	2) "requires" can be told to look for functions in packages external to the current folder.
	3) prepIX_I lists the versions of functions that could be found by the "requires" function.
		See the #****LIST OF REQUIRED IX FUNCTION VERSIONS**** suffixed to the 
		resulting final program.
	4) "requires" in Phase II can very selectively enforce the version control begun in Phase I. 
		(Python's standard "import" lacks all version control of functions).
	
	The author will implement the "requires" functionality in 
	two distinct levels, steps or phases when releasing prepIX,  
	to minimize the steepness of the complexity of "requires".

	In Phase I, "requires" will only accept "v==0" of functions and will 
	only accept the definition of "requires" as built-into prepIX_I.
	
In Phase II (Source 02), "requires" can be modified by users of the IX family of Software, although this is not recommended. Furthermore, in Phase II, the "requires" function can be told to use alternate versions of "requires". Also in Phase II, the version of each function that is inserted into a main program by "requires" can be a version higher than 0 and requires can be very selective about the exact version (or range of versions) that are required for each function. Many extensions of the functionality of "requires" have been intentionally delayed until Phase II of "requires". The intention is that the concept of "requires" and its syntax are introduced in Phase I of the prepIX module, known as prepIX_I. Some programmers will be content with the effectiveness of Phase I of "requires" and will never use the features in Phase II. Programmers who are more senior, can gradually use more and more of the features of Phase II of "requires". Phase II of "requires" is described in detail in Source 02.

Functions, Entities and Macros will be contrasted later in this article, but the "requires" function will be explained first. Note that all macros will be processed (by IXp) before functions are added (as "required") by prepIX_I.

The "requires" Function

When using the "requires" function itself, the program needs the following import statement to be used. The "requires.py" module should be included in the same folder as the main program that is being created.

	from requires import requires
	
Of course, the "requires" function, itself, cannot simply be "required" by a "requires" statement.

The "requires" function has (at least) the following 4 main purposes or uses:

A. Import-like Purpose

The running of the "requires" function will do a similar job to that of an import statement except that the requested function definition statements will be automatically inserted in front of the main program. This will be automatically done by the prepIX_I.py program. The "requires" function (formerly known as the deprecated "requests" function) should specify that version 0 of the "required" function will be used. The use of higher numbered versions of functions is not recommended in Phase I.

B. Function Availability

Proper use of the "requires" function causes prepIX_I to verify that version 0 of the function that is being required is available. An error condition will be raised or a warning issued if the function is not available. When searching for the function, the prepIX_I program will look in the current folder and in the Desktop/IX_assets/ix/ix_pkg.py file. The "ix_pkg.py" file is a package containing all the functions being used by the IX family of software. Users of the IX family of software can also add functions to this package, if they wish. The "textpack.py" IX program (in Source 11) can be used to add functions to this package.

Function Pairs in the IX Family of Software

Every function requested using the "requires" process is actually a function pair. For example, in the IX family, a Python function named "show(s,s,s)" needs two related show functions for "requires" to work. They will be show(s,s,s) and show_v00(s,s,s). Note that the version number (i.e. 0) of the most current show function appears as a suffix to the name "show" in the name of the second function of the function pair. This name is "show_v00". This syntax allows for future version numbers up to 99 to be used. But note that Phase I only supports use of version 0, which is a suffix of "_v00". The simple show(s,s,s) function is what is normally coded by the programmer so that a Python program will invoke the function "show". The show(s,s,s) function merely calls the show_v00(s,s,s) function, which is the current version of the show function. This function-pair permits the programmer to use the show(s,s,s) function without needing to know the current version of the required function. The actual work (or algorithm of the show function) is done by the show_v00(s,s,s) function. The IX family of software will use a similar function-pair whenever "requires" is used. This tells prepIX_I that an external function is needed. As with normal Python coding, in-line functions can still be used with the IX family of software. Instead of coding "from show import show", the user of the IX family of software will code:

	requires("show(s,s,s,'v==0')")
	
The IX family of software needs to know how many parameters "show" will need AND the version of "show" that is required. Note that the version number must be the integer 0 not a floating point 0.0 . Furthermore the 0 following the "==" must NOT be the the string '0' nor "0" which would be enclosed in a pair of single or double quotes. The single quotes enclosing the 'v==0' are needed, as are the double quotes enclosing the whole first parameter. It is important enough to repeat "the single parameter given to the requires function is a string and must be enclosed in double quotes", as shown in the examples in this article. The "requires" function demands that this exact syntax be used.

In the development of software, a number of different versions of a function are often generated over the lifetime of a function, but not at the beginning. In the IX Family of software, in Phase II of "requires", each version of a function should be maintained in Python code in THREE distinct places, as described below. However Phase I of "requires" expects version 0 to be used, so version control can be easily introduced. Phase I expects that higher versions will be avoided. Therefore there is no need to maintain complete version control in Phase I. Much better knowledge of version control is required if Phase II of "requires" is ever used. It is helpful to have a brief preview of the future Version Maintenance in Phase II, as described next. The neophyte programmer might skip the following paragraphs until heading "D".

In Phase II, Version Maintenance will be done in three "places":

1) the actual new version of the module. The version number must be part of the name of the module. In this example, the name of version 0 must be show_v00.py. The code in version 0 of the module will certainly differ from the code in version 1, the next version.

2) no version # in the function name called. the function actually called by the main program must NOT have the version number embedded in its name. But the version number must be specified within the function ( ie in show.py). But the user programmer doesn't need to worry about specifying a version # in any subsequent code invoking show() that he/she writes. See the example below:
	isShow = True
	firstName = "Henry"
	show("firstName", firstName, isShow)
For each function used by the program, it is only the requires() statement that is located near the top of the main program that needs to know the version of show for the main program. This "requires" version number" must NOT appear every subsequent time show() is invoked in the main program. It is only the "requires()" statement that needs to know the version number. The "requires()" doesn't need to know the actual parameters that will be used. This is because "requires" is NOT really used to invoke show(), but it needs to know the number of parameters that show() uses. For this reason, the actual format (syntax) of the "requires()" statement for the "show()" function (near the top of the main program) in Phase I is:
	requires("show(s,s,s,'v==0')")
	
That is really all that the user programmer needs to do (instead of coding an "import" statement. Later in the main program, he/she can code the following to invoke show without mentioning a version number. As can be seen, this is a very normal invocation of the show() function, normal in that it does not mention the version of the show() function that will be used.
	isShow = True
	firstName = "Henry"
	show("firstName", firstName, isShow)
	
Of course, this way, the exact same version of show (provided to "requires") will be used each time show is invoked in one program.

3) ISO9003 - version control. The version control needed by the ISO9003 standard is described next.

C. ISO9003 Software Version Log

The reasons may be quite complex to understand, but (future) version control of software must not be taken lightly. If this is unclear, get assistance to learn more about good version control practices. Unfortunately, most communities using Python do NOT pay enough attention to software version control. Version control is so important that the world's best corporate ISO standards demand that it be addressed, be given priority and be maintained as shown next.

Therefore, the third place where the version number of software must be maintained is in the "ISO9003_Software_Version_Log.txt" file. A prerelease sample copy by the author for the IX Software Family can be seen in Source 08. Such version control is only absolutely necessary when using Phase II of "requires".

(simplified)ISO9003 Software Version Log
**************************************************************************************
Software module
Name		Vsn Parms Hash	  Date(YMD)	      Description
----------	-   ----- ----	  ------------        --------------------------------
. . . .

commandOS.py	0    1      N     2024CMAR14	      function used to submit an OS 
							command and return the results

inputWto.py	na   2	    N	  2023AJan01	      function used to request input 
					  		With a waiting time-out
requires.py     0    1      N     2023KNov25          function used to "import" python
                                                        modules using version control
show.py		0    3      N     2023KNov25	      function (debug) used to display 
							the value of a variable.
. . . .

The latest version of ISO9003_Software_Version.txt can be seen in Article 215.  Its format
has been improved slightly.

/ISO9003_Software_Version_Log.txt
*************************************************************************
Now, returning back to the use of the prepIX_I.py program with Phase I of "requires".

D. Version list of required functions used in the main program

The prepIX_I program will also generate a list of the required functions (mentioned in a program) and the actual versions available. This list appears as comments appended to the bottom of the source code generated by prepIX. This list does NOT include the names of any macros used, only functions. An example of this list is:

	# ****LIST OF REQUIRED IX FUNCTION VERSIONS********
	# Highest	Loaded    Required             MD5
	# Vsn Avail.	Vsn 	  Vsn                  Hash
	# ----		----	  -------------------- ----
	# v00		v00	  show(s,s,s,'v==0')   Pass
	# none		none	  inputWto(s,s,'v==0') none
	# *************************************************
Note that a "none" appears in the column(s) if a "required" function-pair cannot be found/used.

The 'v==0' causes prepIX_I to almost completely ignore automatic version control. In Phase I, each "requires" statement encountered will have the form of:

	requires("func(s. . 'v==0')") 
	
Each such statement is converted into 3 statements that will do a similar action as the first 3 statements shown below:
	from ix_pkg import show
	from ix_pkg import show_v00
	requires("show(s,s,s, 'v==0')")
	. . . . .
	isShow=True
	firstName="Henry"
	show("firstName",firstName,isShow)
	. . . . .

	This will prepend the function-pair (found in ix_pkg.py) to the main program 
	instead of importing it normally. (The normal imported statements are NOT visible).
	
As can be seen, the "requires" function and the prepIX_I program are used together as one of the most important components of the IX family of software. The user programmer must code the "requires" statement and the subsequent "show" statement(s) that invoke the "show" function. However, in Phase I, the "requires" function can sometimes be completely disregarded (unused), if the user programmer wishes to produce shorter less complex Python programs. Both "requires" and "import" can be used together, but using both can lead to confusion.

The advanced version of prepIX in phase 2 is able to verify the MD5 hash total of each module that is required. Verification of an MD5 Hash total will result in one of three verdicts: "Pass", "Fail" or "none". The verdict (or result) will appear in the LIST OF REQUIRED IX FUNCTION VERSIONS (shown above). The prepIX_I.py module will always show a result of "none" regardless of the presence or absence of an MD5 hash total in the source code for the function that the code "requires".

Written Code vs Generated Code

A Python program is made up of a list of statements commonly referred to as "code". Code is run (executed) on a computer. The path taken by the computer when executing code is called a thread. The thread always begins at the top of the code. The thread stops when an exit() function is encountered or when the thread reaches the bottom of the code. Sometimes the thread encounters an "endless loop". Execution continues forever in an almost endless loop, until the value of a variable changes to make it encounter an exit() function. Other ways to stop a program are to hit ctrl-c on the keyboard or to shut-down the Operating System or to Turn Off the Power to the computer.

Most code is written by a programmer (not automatically generated). Each program is usually unique. Because of this, the programmer must write most of the statements, at least those that are in the main module.

Some code in a program can be automatically generated or inserted into the main module without the programmer coding it line-by-line. There are three types of generated code:

Functions, Entities and Macros

FUNCTIONS, in Python, are COMPLETE FUNCTION DEFINITIONS beginning with a "def" statement and followed by a function name. It is also possible to import a function before it is referenced. The statements that comprise imported functions are not usually visible in a listing of the main program. Each function definition or import statement must be physically located in the main module in front of (physically preceding) any statements that reference the function. Usually such a reference actually invokes the function. A function can be a routine that returns one or more pieces of information, but it is not imperative that a function return any value or anything at all. Functions not returning anything are often called subroutines. Functions usually terminate their execution of a thread with a "return" statement that returns run-time control (or the thread) to the program that invoked the function. Each time a function is invoked, the exact same set of statements in the function are used, however each thread through a function might take a different path through the function. A program execution thread usually travels completely through a function although sometimes threads can be created or interrupted. A function is presented with zero or more input parameters each time it is called. The values of these parameters can often be different each time the function is called (invoked). The actions taken by a function are determined by these input parameters and by other information that the function encounters or gathers during its execution.

ENTITIES are usually assigned a value by the programmer before the macro modules are invoked (converted into code.) (A macro is defined in more detail in the next paragraph.) A macro can be cited in various different instances (even multiple times) in a program. Often each instance is different. It is the different value of the entities used by a macro that cause the macro to generate different code. In IX macros, the use of an entity appears in one macro definition (or place in the code) and also appears in another macro invocation or place. Sometimes an entity is not defined at all, but an attempt is made to use it; this is seen as an error and causes an error to be thrown during the evaluation phase of the macro preprocessor, although the IX software could be changed to simply consider an undefined entity as a null string. On any code line that invokes a macro, most of the entities used by that macro will be defined or will have already been defined by the user programmer. In the current IX Software Family, all entities must be predefined by a macro or by the programmer before they can be used to generate code in a macro. Often, the macro invocation is immediately followed by a "?" and the entity values. In IX macros, entities can be defined in one macro and used in that macro or in another macro or never used at all. Some macro systems set all entity values to "undefined" at the end of each macro generation instance, IX macros don't do this. In fact, an IX macro might only assign values to entities and might not generate any code at all. All entity values are forgotten immediately after the LAST macro is used to generate code. Entities have no value, use or meaning, once the finished program has begun to be processed by Python. In IX macros, the format of an entity name is "&item|". Every entity must begin with "&" and a lower case alphabetic character, then any alphanumeric characters (and a few others eg "_" and "-") are allowed, finally ending with a solid vertical bar ("|") as the last character of the entity name. Programmers using IX macros should avoid defining entity names that begin with "&ix_" because such names are reserved for use by the IX macro system itself.

MACROS generate one or more lines of code before the program begins to run (processed by Python). Such lines of code are defined in a macro definition module. Some people say that macros must be used by a pre-processor before the final program generation is complete. The lines of macro code generated by the preprocessor do NOT normally begin with a "def" and do not normally contain a "return" statement. Some macros only generate data, variables or entities, in which case an execution thread might not be understood to travel through a macro. A single macro might generate vastly different code each time it is invoked (used to generate code). In the IX software family, a macro always generates full lines of code, not partial lines of code. At code generation time (before run time), an invoked macro can be presented with zero, one or more entity value definitions. Each entity is usually a small string of characters (or a number) whose value usually remains constant during the evaluation phase of the macro. An entity was defined in more detail in the previous paragraph. Based on the content (ie value) of each entity, the macro can generate very different lines of code, or perhaps no code at all. After the evaluation phase of prepIX_I is complete, the resulting (generated) code is finalized by the prepIX_I preprocessor. Afterwards, this generated code never changes during the execution of the program.

Within the IX Family of Software, there is a Control file named "ix_eDict.txt". The values of the entities within this file control some of the main actions in the IX Software Family. The user of the IX Software Family is encouraged to NOT change "ix_eDict.txt"

Invocation (Use) of a Function or an Entity or a Macro

A function and/or an entity and/or a macro can be invoked or used multiple times in a program. Each such use is called an invocation of the function, entity or macro. The flow of control (sometimes called the thread of execution) always passes through a function, an entity or a macro. Even when data is defined without executable statements, the flow of control will pass through these data-defining statements. A function or an entity or a macro can be mentioned (or used) zero, once or multiple times in a program or in another function or macro in the same program. Each such occurence is called an invocation of the function or entity or macro.

The use (invocation) of a function name that is not preceded by "def " always is exactly one statement that is sometimes referred to as a function call or invocation. However, each invocation of a macro can generate zero, one or more statments.

Zero, one or more parameters can be passed into a function. An example of a function invocation that is passed exactly three parameters is shown in the example below:

	show("firstName", firstName, isShow)
	
This "show" function was created to result in the generation of a print statement used for debugging purposes. The statement(s) that will be run by the above "show" function will be the equivalent of:

	if isShow :    print("firstName: ", firstName, ":")
	
The variable "isShow" and "firstName" must have been defined prior to invoking the show function. A valid use of the show function can be seen below:

	isShow = True
	firstName = "Henry"
	show("firstName", firstName, isShow)
	
When appearing in a program, these statements will cause the equivalent of these statements to be run:

	isShow = True
	firstName = "Henry"
	if isShow :    print("firstName: ", firstName, ":")
	
These statements, when executed, will display on the screen:

	firstName: Henry:
	
This display informs the programmer of the current value of the variable "firstName" because isShow is True. If "isShow" is set to False before a show statement is executed, the show statement will display nothing. During debugging, "isShow" is usually True. When debugging is complete, during use of the finished program, "isShow" is usually set to "False", causing nothing at all to be displayed by the show function. The function "show" is a very useful debugging tool, and it can even be left in the finished program, available to be used again if another debugging run is found to be necessary. To use it again, simply set "isShow=True". The author usually places a single "isShow=False" near the beginning of the program after removing all other "isShow =" statements from the final main program. There is an IX Control entity named "ix_isShow" that can be set to True (in the external control file ix_eDict.txt). The intention of the author is that ix_isShow will override the value of isShow everywhere in the program.

Often a function will return one or more result(s). The results of a function are usually placed into variables by a statement that contains a single "=" character. Usually, there is only one result, but zero or any reasonable number of results are permitted. These results immediately follow the word "return" (prior to the line feed at the end of the return statement) within the function definition as shown below:

	return firstName
	
A macro causes statements to be generated, no variables are ever returned. An IX macro is never invoked in a statement immediately followed by an "=" character (although "=" signs can appear later in the statement that invokes a macro). However entities can be passed into an invoked macro in a similar but not identical manner to the passing of parameters into a function. A programmer begins the definition of a macro by assigning the macro a "macro file name" followed immediately by a "|". A macro definition is usually immediately followed by an invocation of this macro. The macro invocation can be followed by a "?" and an entity definition as shown below, but a preceding macro definition file-name must always be immediately followed by a "|". It is recommended that all IX macro names end with the string "_ix.py". An example of an IX macro invocation named showIt (being passed one entity definition) and immediately preceded by its macro definition is shown below:

	showIt_ix.py|
	show("&nam|",&nam|,isShow)
	showIt_ix.py?nam=firstName
	
If the correct macro definition is predefined, found and used by the preprocessor named prepIX, the macro invocation shown above will be used to generate the following line of code. The prepIX_I preprocessor will examine the definition of the macro named showIt_ix.py . Then prepIX_I will find and replace every occurance of the five characters "&nam|" with the character string "firstName" which is 9 characters long because the double quotes are not part of the search-string nor the replacement-string.

	show("firstName",firstName,isShow)
	
Once having been defined, either in the main program or external to the main program, the macro definition named showIt_ix.py can be invoked again. The following statement (immediately below) will be analyzed by prepIX:

	showIt_ix.py?nam=lastName
	
After analysis, prepIX_I will replace it by the (generated) single statement shown below:

	show("lastName",lastName,isShow)
	
Note that the "|" character is not needed when invoking a macro. Only a few keystrokes are economized by the use of a macro in the above example, but the macro concept can provide much efficiency for any programmer. Note that the name of the macro ("showIt_ix.py") must be stated when the macro is invoked, even if the macro is only defined in the main program. Furthermore, the macro name can be totally different from the actual characters that are defined in the macro definition (and hence those generated by prepIX.)

Note especially that the full file name (eg showIt_ix.py) of the macro definition must be used when invoking the macro, even if the macro does not appear in any folder nor package. The use of this name makes it possible to eventually house the macro definition external to the main program. Macros are often housed in the current folder or in an IX software package. Often IX macros are stored in the package named "ix_pkg_ix.py". Note that the name of the IX function library is different: "ix_pkg.py". The full list of IX_Control information can be found in [editor's Note: tba].

It is important to fully understand the difference between a macro definition and a function definition. The definition of a function must always occur (either using a "def" statment) or at least be mentioned in a "requires" or "import" statement before it is first invoked. (Exceptions are the built-in commonly-used Python functions such as "print()". The definition of a short macro does not normally contain the letters "def " and usually occurs immediately before the first time it is invoked. But a macro need not be defined in the program where it is invoked. Instead, the macro definition (especially a longer macro re-used by more than 1 main program) can be stored in the current folder where the main program is stored, or in a library of packaged modules. Names of IX macros must always end with "_ix.py" which is a name suffix that is reserved by the IX Software Family. Non-macro names (of functions, programs or text files etc) should never end in "_ix.py".

Some Python functions can be invoked without being previously mentioned. These are called built-in Python functions. Examples are print(), int() and exit(). Python permits access to external libraries containing functions. Such functions must be mentioned in an "import" statement before being invoked. The programmer is permitted to create IX functions that are stored in a folder or package. These functions must be mentioned in a "requires" statement in the program module before they are referenced (invoked). The prepIX_I preprocessor will search for all "required" functions in special folders external to the source code module. The prepIX_I preprocessor will also accept macro definitions defined within the main program or defined in folders outside the main program. The prepIX_I program will recognize an invocation of a macro by the suffix of the macro name which is "_ix.py" that is NOT immediately followed by a "|". Following "_ix.py" by a LF or EOL is recognized as an invocation of a macro. Comments should NOT immediately follow an IX macro invocation.

	 WARNING - programmers must NOT code the string suffix of "_ix.py|" unless defining a macro
	
If a programmer absolutely needs to code "_ix.py|" when NOT defining a macro, it can usually be coded as "_ix.py"+"|". The prepIX_I preprocessor will recognize the end of a macro definition by the invocation of the macro, or by the string "&ix|" (followed by a LF) which indicates the end of a macro definition. The prepIX_I program searches for both external IX functions and for external IX macros using exactly the same search algorithms and search paths.

Do Macros Invoke Functions (or vice-versa)?

A macro can invoke one or more functions. But a function can also invoke one or more macros. This results in the age-old question: Which came first: the chicken or the egg? One can envisage a macro that generates a function definition or invocation; then this function can invoke a macro . . . ad infinitum. The IX Software does NOT allow this. The manner in which this is enforced is described next.

The IXp pre-processor is told to "process" an initial list of modules ending with the "main module". Any functions that contain macro invocations must appear in this list of modules. These modules are combined together into one basic set of concatenated code statements that contain no "generated" code (yet). No macros will have been processed (yet) and no "requires" statements will have been used (yet) to cause the specified (required) functions to be included in the basic set of code statements. It is this basic set of code statements that will be used as input to the macro preprocessor. Any of the functions (or the main program) in the basic list of modules can contain entity and macro definitions and invocations. These entities and macros (in the basic list of concatenated modules) will be processed by the macro pre-processor. Any functions mentioned in "requires" statements will not be discovered (and will not yet be concatenated to prepend the basic set of statement) until after all macros have been fully processed.

This means that functions mentioned in "requires" statements must contain neither definitions nor invocations of any entities or macro. Such functions are usually the least complex functions. Any functions invoked by a macro must have either 1) appeared in the basic list of modules presented to the IXp preprocessor or 2) contain neither entity nor macro definitions. Any function modules that do not meet this condition must be included in the basic list of modules that is presented to IXp.

A complex main program is often subdivided into a number of subroutines or functions. Some of these parts (modules) of the complex main program might each need some macro-processing. Such parts of the complex program should be included in the initial (basic) list of modules that precede the complex main program module. Usually all entities and macros precede the complex program segments, functions or subroutines.

This may seem to lead to complexities that are impossible to resolve, but past experience has shown this to be quite simple to accomplish. Most common functions (found in packages or libraries) do NOT contain any macros. Such common functions can easily be mentioned in "requires" statements in the main program (or in segments of the main program that are listed in the initial basic list of modules.) Programmers having difficulty with this "chicken and egg" restriction should request assistance and/or advice from more experienced programmers.

If any entities or macros have not been resolved when automatic code generation is complete, they will result in error messages being thrown by prepIX_I or prepIX.

Searching For External Macros and Functions

Some IX macros can be defined externally to the program where they are invoked. The prepIX_I preprocessor will search elsewhere for all macros that are invoked without being defined immediately before the first invocation. The prepIX_I preprocessor will search for such macro definitions in folders external to the main code module.

Whenever a program is being preprocessed or run through Python, a Command Line Interpreter (CLI) statement can be used to initiate this process. Such a CLI statement can accept the names of files containing code segments (modules) before the name of the main source code module (which must be the last module named). All external function definitions or external macro definitions appearing in the CLI statement will be concatenated to (before) the code in the main source code. An example of such a CLI statement is shown below:

>$ IXp -python3 showIt_ix.py show.py Test_show.py

The above CLI statement will cause Python3 to evaluate, concatenate and run these three modules. This will cause the showIt_ix.py macro definition module and the show.py function definition module, containing the show() function definition, to prepend which means to be concatenated to (in front of) the Test_show.py module. To be found by Python, all three modules: the showIt_ix.py, show.py and Test_show.py modules are often located in the current folder. Unfortunately, the CLI command above will probably not function as expected because Python3 does NOT automatically invoke the prepIX_I preprocesor. For the above statement to function correctly, the IXp module must automatically invoke prepIX_I befor invoking Python3. This capability is part of the future plan for the IXp program. Perhaps the CLI statement shown below will operate correctly one day but not yet:


>$ IXc -prepIX_I showIt_ix.py show.py Test_show.py

Repositories of IX Functions (and IX Macros)

Functions are a very important, even critical, part of Python programming. Functions are a major part of the software created when using most if not all programming languages.

Macros are similar to Functions, but are not a normal part of Python. Macros can simpify programming tasks, but macros can be ignored and not used at all. Objects, which differ from variables, appear in Python and many other popular programming languages. Objects have been implemented in Python. These objects differ from variables; they are more powerful than variables. Special object methods can be defined; they are special functions that only work on objects. Object definitions can be imported in a similar manner to functions. Programs written to make major uses of objects are called Object-Oriented-Programs (OOP). The IX macros can be used with objects. An example of an Object that is often used on a Raspberry Pi is the RPi.GPIO (or RPi5GPIO) group of objects that are used to reference and access GPIO pins. Objects will not be explained any further in this article.

In Raspberry Pi Python programming, there are at least 8 different repositories of function code:

   1. functions inherent to Python (approximately 80 built-in Python functions)
		e.g. exit(), int(), input(), len(), print(), range() etc
	All other functions must either be imported or defined within the main program code
	The statements defining these built-in Python functions are never visible to the user.
   2. functions in external libraries (eg on the web, accessible to all users of Python)
	They must be "imported" into a module to be used.
	     e.g. import time, sys, GPIO
	e.g. time.sleep(1), sys.exit(), GPIO.output(4,1)
	 All imported functions are NOT visible in the main program code.
  *3. in-line functions that accompany the source code of the main program
	These are defined by the user. Statements defining each of these
	functions appear as main program code and are visible to the user.
  *4. functions stored in the current folder on the user's computer
	The current folder is the folder housing the main program.
	These functions are either written by or possessed by the user
	These statements are visible to the user.
  *5. functions stored in user-accessible folders on the user's computer
	These functions are often written by or possessed by the user.
	These statements are visible to the user.
  *6. packages containing groups of functions
	These packages are groups of functions combined on a computer and often 
	restricted to specific users.  Each Python system (eg Circuit Python
	and Thonny) does this differently.
   7. functions stored in folders with user-defined paths
	Code "sys.path.append('folder') to add a user-defined system path.
	Other system paths can also be defined. Note that sys must be imported.
   8. latest functions stored in an article
        The most recent ISO9003 file can be found in Article 215.
A Raspberry Python programmer can add an additional repository folder by prefixing his main program code (for example) with the "sys.path.append" statement shown below. As shown below, it would be appropriate to also include the statement to import the "requires" function when using Phase II of prepIX.
	import sys
	sys.path.append("/home/pi/Desktop/IX_assets/ix")
	#from ix_pkg import requires #(remove the leftmost "#" for Phase II)
	#from ix_pymain_pkg import Test_show #(remove the leftmost "#" 
	#   to use main program code in ix_pymain_pkg.py)
	#/prepIn.py
	
Many, many of the author's small Python programs are stored in "Desktop/IX_assets/ix/ix_pymain_pkg.py". Most do not use version numbers. They have all been packed (stored) in the ix_pymain_pkg.py package to reduce clutter on any Raspberry boot drive, whether the boot drive is a microSD card or an SSD. As of 2023KNov08, the drive containing the most of the author's IX functions is the SABRENT01 512GB SSD Raspberry drive. The fourth line above is a statement that illustrates how easy it is to access and run a single main Python program of the IX Family of Software.

A very simplified version of ix_pkg.py package can be seen in Source 07.

The IX Software Family only supports those repositories marked with an asterisk in the above list.

The following sample program makes full use of the features of Phase I of the IX Software Family. The program named "Test_show.py" shown below has been coded. It is stored in the IX package named "ix_pymain_pkg.py". The function pair, show.py and show_v00.py, have been stored in the package named "ix_pkg.py". The prepIX_I.py program has been placed in the sandBox folder (which is in the Desktop folder). The following Python statements are temporarily stored with the name "prepIn.py" and are read by prepIX_I from Linux stdin. The statements in prepIn.py are used to tell prepIX_I where to find the name of the program to be prepared. In this case, this name is, of course: "Test_show.py" :
	import sys
	sys.path.append("/home/pi/Desktop/IX_assets/ix")
	from ix_pkg import requires
	from ix_pymain_pkg import Test_show
	#prepIX_I will use Test_show.py to create main.py
	#/prepIn.py
	
This code named "prepIn.py" will be processed by prepIX_I, the resulting Python program will be stored in the sandBox folder with the name "main.py" . The user should then run this main.py program through Python. The user can do this using the following CLI statements in Thonny or in Terminal on the Raspberry Pi:

	>$ cd /home/pi/Desktop/IX_assets/sandBox
	>$ python prepIX_I.py <prepIn.py
	>$ python main.py
	
The prepIX_I program will generate (create) the final program named "main.py". When run by Python, it will display the following output:
	firstName: Henry:
	
To view the resulting valid Python program (that was generated by prepIX) named "main.py" that ran through Python, simply display it using "> $ more main.py" or "> $ cat main.py". Either one will display:
	>$ more main.py					
	import sys					(from Test_show.py)
	sys.path.append("/home/pi/Desktop/IX_assets/ix")  .. (ditto)

	from requires import requires			(from Test_show.py)
	#from ix_pkg retrieve show	      		(generated by prepIX_I as a comment)
	#   listing of show.py will follow		  ..
        def show( etc					(generated by prepIX)
	    . . . . 					  ..
	    return					  ..
	#from ix_pkg retrieve show_v00      		(generated by prepIX_I as a comment)
	#   listing of show_v00.py will follow   	  ..
        def show_v00( etc				(generated by prepIX)
	    . . . . 					  ..
	    return					  ..
	#from ix_pymain_pkg import Test_show		(from prepIn.py as a comment)
	requires("show(s,s,s,'v==0')")			(from Test_show.py)

	isShow = True					(from Test_show.py)
	firstName = "Henry"				(from Test_show.py)
	show("firstName",firstName,isShow)		(generated by prepIX)
	# ****LIST OF REQUIRED IX FUNCTION VERSIONS****	(generated by prepIX_I as a comment)
	# Highest     Loaded    Required	   MD5	  ..
	# Vsn Avail.  Vsn 	Vsn		   hash   ..
	# ----	      ----	------------------ ----	  ..
	# v00	      v00	show(s,s,s,'v==0') Pass	  ..
	# *********************************************	  ..
	#/ main.py      				(generated by prepIX_I as a comment)
	
At first glance, all of the above statements appear quite complex. But closer scrutiny makes us realise that the above code (in sandBox/main.py) contains only 2 function definitions and the 4 executable statments shown below:

	requires("show(s,s,s,'v==0')") 
	isShow = True
	firstName = "Henry"
	show("firstName",firstName,isShow)
	

The only reason for executing the first ("requires") of the 4 statements, is to make it possible for the "requires" statement to throw an error message, if necessary at run time. Successful programs that use the "requires" functionality will "silently" execute the "requires" statement. The word "silently" means "by not displaying any message". All original comments will have been suppressed by prepIX_I in order to reduce the size of the resulting "main.py" code. The prepIX_I program will have added all of the comment statements in the resulting "main.py" program shown above.

Upon reflection, the most important statement in this whole main.py program is:

	firstName = "Henry"
	
All of the rest of the code provides a rich environment that facilitates Python programming and debugging on a Raspberry Pi.

If the above functionality appeals to you, welcome to the IX Software Family. If you don't see anything above that is worth using, then the IX Software Family is probably not for you. Happy Python coding anyway !!!

The full list of components of the IX Software Family can be found in "ix_eDict.txt stored in Desktop/IX_assets/ix" on SSD _KIOXIA_GTLL using any of my Raspberry Pi 4 or 5 computers.

Invoking the IX Macro Processor

Note: The FRUN function has been deprecated by FRUM (see Source 15: article 217).

This addition to Article 209 (by D@CC on 2024CMar13) was after a hiatus of a few months. However much has occurred since the original article was written. The Raspberry Pi 5B has been delivered and the author has made great progress on developing his software repository in Article 215 and the various package libraries in the IX Software system. It seemed appropriate to document the author's latest thoughts on the IXp processor. The "requires()" function has progressed very slowly, but it seemed more important to create the FRUN subroutine first. The portion of this article (up to "Other Related Thoughts") describes how the author plans to use the IX Software when preparing final python code for important applications. The use of the FRUN function will now be a major part of the IXp macro processor. The remainder of this article will describe how repeated use of the FRUN function will create an application by making use of macros, required functions and stubs of code that will be combined into a full application. A list of FRUN invocations will serve as a batch file that will generate a final set of python code in one code module. The remainder of this article will hopefully help the reader to understand how the list of FRUN functions will generate a full python application ready to be run on any Raspberry Pi, Raspberry Pico, Wio Terminal etc. Please read on.

The IX Macro Processor assembles a number of code modules concatenating them together, storing them in the current folder of the operating system, assigning the result a name and usually finishing by running them through python before the resulting program is finally run. Sometimes only one code module is used. Often two or three modules are used. Sometimes many are modified only slightly. Each specified code module is located in-line or in the current folder or in a package of modules. There are numerous packages that can house the modules. Therefore the place housing each module must be specified. In the IX Software system, there are three main types of modules that are combined to make a final program. The order of the modules is important. First a full list of needed functions is created. Then this list is compared to the list of defined functions (whose code is available). Using these lists, a list of "missing" functions is prepared. The source code definitions of all the "missing" functions is sought and those found are included in the final code before the functions after all the macro definitions. The specified macro modules must be included first, then the code for the required specified functions is included, then the non-macro, non-function stub modules are added. Placed in this order the code modules form the main program.

The macro modules often require one or more entities to be defined for each invocation of the macro module. For the IX Software system, the number of incoming parameters and the version of each function module must be specified. Finally, a name must be asssigned to the final set of code. This name either defaults to the name of the last code module stub or a new name must be assigned.

Although not yet coded, there are 3 possible ways of invoking the IX Macro Processor:
       
  1. single IX statement CLI:
     >python IXp abc_macro.py ghi_macro.py?name=Joe funcA.py funcB.py t02.py | prepIX | python 

  2. interactive>IX where the IXp system prompts the user for the name of each subsequent module:
	IXp.py <
	abc_macro.py
	ghi_macro.py?name=Joe 
	funcA.py 
	funcB.py
	t02.py | 
	prepIX | 
	python 

  3. batch IX
     >python IXb t02_batch.py

     (where t02_batch.py is:)
	abc_macro.py
	ghi_macro.py?name=Joe 
	funcA.py 
	funcB.py
	t02.py | 
	prepIX |
	python 
Note the use of the "|" piping operator. The above examples unfortunately do not specify the source of each module, whether they be in-line, in the current folder or in a specified package. Function modules can be recognized by their prefix of "func" in these examples, but such prefixes are not necessary. Macros can be recognized by their suffix of "_macro" although such suffixes are not necessary. Note that function names specified never include an extension of ".py" even though their source modules often do include such a suffix. But macro modules invocations must always be followed by an extension of ".py". The remaining python modules are usually code stubs. A stub is some python code that must be accompanied by other modules for the result to be a valid code module. The stub modules usually do not contain many macro statements to be expanded for them to become valid python code. A macro module always needs at least one entity to be defined for it to become valid python code. Without mention of an entity, a macro is merely a stub of code. Each function usually does not have any macro references, but they are allowed in the IX Software system.

For a macro module to be usable, all the entities used in it must be defined, otherwise code errors will be thrown. by Python. In each function module, the incoming parameters must be specifed in the definition. When a function is invoked, the value of each incoming parameter must be assigned. After each code stub is processed by the macro processor all entity placeholders (entities that are used in it) must be defined, otherwise code errors will be thrown. Often, there are no macro entities in functions, but any entity placeholders in functions must also be defined.

Some examples of the absolute simplest IX program definitions are:
  
  1. single IX statement CLI
     > python IXp abc_macro.py funcA.py t02.py

  2. interactive>IX
	IXp.py <
	abc_macro.py?name=Joe 
	funcA.py 
	t02.py | 
	python

  3. batch IX
     >python IXb t02_batch.py

     (where t02_batch.py is)
	abc_macro.py?name=Joe 
	funcA.py
	t02.py | 
	python
	#/t02_batch.py
In all cases, prepIX is necessary to process any requires("s, 'v=0' ") statements coded into t02.py . To simplify the invocation of python programs written for IX Software, the 2 phrases "prepIX |" and "python" will be added by IXp if they are absent.

Note 01: prepIX will always add (to the main program) the path

    sys.path.append("/home/pi/Desktop/IX_assets/ix")

    This statement will cause python to search this path for any missing (undefined) code modules. Such code
    modules can be either function definitions or class definitions.

Note 02: prepIX will always search the specified package if the following statement is coded.

    IX_append("/home/pi/Desktop/IX_assets/ix/pkg_py.py")
     or (to simplify coding [only for pkg_py.py])
    IX_append()

The IX_append() automatically uses 'IX_append("/home/pi/Desktop/IX_assets/ix/pkg_py.py")', but no other path or folder.

Note 03: prepIX will always search all IX packages in folder "ix" if the following statement is coded.

    IX_append("/home/pi/Desktop/IX_assets/ix/?pkg*.py")

Note 04: For IX software, it is desireable to code function pairs [eg show() and show_v01()] that 
have been created for future IX version control. IX version control uses the "requires()" and 
prepIX modules. To make use of such function pairs without the "requires()" and prepIX modules, 
simply import them into python using the following statments:
from show import show from show_v01 import show_v01 To import them into Thonny, it is necessary to concatenate them both together in a code module package and give it a name (eg show_modules_pkg.py). Then import them using:

from show_modules_pkg import show, show_v01 After installing the future IX Software modules: requires() and prepIX.py, the import statement(s) should be replaced by "requires()" statements.

Alternative CLI Source Locations

The example code statements under this heading have been deprecated by use of the FRUN function. This material here will soon be edited.
  1. single IX statement CLI forms:
     >python IXp abc_macro.py def_macro.py?name=Joe funcA.py funcB.py t02.py | prepIX | python 

	requires("spkg_py.py?m=ix_eDict.txt")		#include a text file defining the IX_Software entities (article 211)
	requires("spkg_py.py?m=abc_m.py")			#include a pkg:macro without any entity definitions
	requires("s?m=ghi_m.py")				#include a currentFolderOrInLine:macro without entity definitions
								(The exact syntax has not yet been defined.)
	requires("spkg_py.py?m=opq_macro.py?name=Joe")	#include a macro that does need entity definitions
	requires("funcA(s,s,s,'v01')")			#specify a versionned function with 3 parameters
	requires("spkg_py.py?s=t01.py")			#specify a stub of python code
	if __name__ == "__main__"			#run the following main statements to test the code above
	requires("mpkg_py.py?main=m1A.py")		#runs this stub of python code (unless whole module is included)

Two packages are clearly specified in the above program definition: spkg_py.py and mpkg_py.py

A third package (of functions) is implied in the fifth statement.  The IXp system will automatically search
	pkg_py.py if the function named "funcA" is not defined in-line nor in the current folder.  If the function
	is still not found, python will search for a function named "funcA" using the python 
	sys.path.append(....) statement if this statment appears in the final set of program code.

FRUN: From . . Use . . Name (A single complex example)

	The following example defines a valid multi-line runnable python code module.  It makes use of IX textpack packages.

	> IXf appA_batch.py				#IXf uses appA_batch.py to create the appA application also named main.py
        #(where appA_batch.py is defined as:)		# the "/" in parm1 indicates that one or other phrase must be specified or ""
	#						#note: each IX macro must have an extension of ".pyix"
	FRUN(mpkg/"mpkg_py.py",ix_eDict.txt)		#include the eDict macro that only defines IX entities with no code
	FRUN(mpkg/"mpkg_py.py",abc_m.pyix)		#include the abc pkg:macro (possibly missing some entity definitions)
	FRUN(mpkg/null,ghi_m.pyix)			#include a currentFolderOrInLine:macro named ghi without entity definitions
	FRUN(mpkg/"mpkg_py.py",opq_macro.pyix?name=Joe)	#include the opq macro that does need entity definitions
	FRUN("requiredFunctions","automaticallyHere")	#all expanded macros and required functions are inserted here by IXf
							# but the expanded macros will not be executed here, invoke them in func/main
	requires("funcA(s,s,s,'v01')")			#specify a versionned function (with 3 parameters) that is required
	FRUN(apkg/"apkg_py.py",t01_py.py)		#specify a stub of python code,but suffix "ix" if it contains macro invocations
							# the final ".pyix" suffix will prevent python from attempting to process it
	FRUN(apkg/"apkg_py.py",appA_py.py,"main.py")	#resulting code will have the name of the last module 
							# ie appA.py (but without the "_py")
							# but a third parameter will also overwrite a 2nd code module named "main.py"
	if __name__ == "__main__" :			#if True: run the following statements to test the code above
	    FRUN(apkg/"apkg_py.py",appA_py_test.py)	#runs this stub of python code appA_py_test.py to test appA_py.py
	#if end						#    (unless the whole module is included elsewhere.)
	FRUN("python","run","end")			#this final FRUN statement causes the module to be run through python and executed
	# results stored in the "/home/pi/buIX/" folder	# The pathed buIX folder is defined as entity "&ix_buIX|" in ix_eDict.txt
	#/appA_batch.py					#the name of this batch file (used by IXf to create this code module)
	

Some simple CLI FRUN examples for IX

	Each of the following examples is a valid runnable python code module.  Some of the examples
	  unpack a module from a IX textpack package.

	FRUN(apkg,t01.py)			  #unpacks "t01.py" from "apkg_py.py" & runs through python, names the result "t01_main.py"
	FRUN("python","run","end")

	FRUN(apkg, t02.py,"main.py")		  #unpacks "t02.py" from "apkg_py.py" & runs through python, names the result "main.py"
	FRUN("python","run","end")

	FRUN(mpkg,abc_m.py,"appC.py")		  #unpacks macro "abc_m.py" from "mpkg_py.py" & runs through python, 
	FRUN("python","run","end")		  #  names the result "appC_main.py" 

	requires(funcA(s,s,s,'v01'))		  #specifies a versionned function with 3 parameters, prefixing it to the next code module,
	FRUN(mpkg,abc_m.py,"appABC.py")	  	  #unpacks macro "abc_m.py" from "mpkg_py.py", runs through python & names the result "appABC.py" 
	FRUN("python","run","end")

	requires("funcA(s,s,s,'v01')")		  #specify a versionned function with 3 parameters, prefixing it to the next code module and
	if __name__ == "__main__" :		  #runs the following main statements to test the code above
	    FRUN(apkg,tfuncA.py,"")		  #runs tfuncA (a stub) of python code (unless whole module is included)
	FRUN("python","run","end")                       tfuncA will should invoke funcA eg funcA("nam",3,4) to test it

	requires(funcA(s,s,s,'v01'))		  #specify a versionned function with 3 parameters, prefixing it to the next code module,
	if __name__ == "__main__" :		  #not naming it but then running (unless the module is imported) the following
	    print(funcA("Joe Smith",3,9))	  #     python code statement(s) to test funcA using parameters "Joe Smith",3,9
	FRUN("python","run","end")

	FRUN("",ghi_m.html?name=Mary,"stub.html") #macro processes ghi_m.html?name=Mary from the current folder, names the result "stub.html" .
	FRUN("end")                		  #  This only generates code, but doesn't actually run neither python nor the result.

	

Caveats for IX Software (mainly regarding sequencing)

The IX Software macro definitions, entity value assignments, function definitions and other code modules must be prepared in a specific order. They must be defined as follows in the batch file presented to IXf:
1. all macro definitions (FIRST)
2. all function definitions (SECOND)
	In-line function definitions are allowed in stub code
	but must appear before the function is invoked.
3. all (non-macro, non-function) stub code definitions (LAST)
4. macro invocations can appear anywhere (in function definitions
	or in stub code definitions) but must be after the
	definition of the macro and must be after the assignment
	of all entities used in the macro invocation
5. entity value assignments (character strings) can be anywhere 
	but every entity value assignment must occur before 
	all macro invocations that use it.
6. any necessary (but unassigned entity value) will prevent a 
	successful macro expansion.  The user (the coder) 
	must ensure that each entity is defined by changing 
	the code (as defined in the modules mentioned in the
	batch file) in order for the IX Software to be error-free.
	In a final version of the IX Software, the user will be
	prompted to provide up to 10 missing entity definitions.
	The entity definitions provided will be automatically
	inserted at the very beginning of the final code module.
7. any function that is invoked must either be previously defined 
	in a code module (in the batch file) or in-line in the 
	stub code or be in a IX software library (package) or be 
	satisfied by python library searches. The absence of a 
	necessary function will throw an IX Software error.
8. When a macro or function is looked up in a package, the
	LAST module containing the macro or function will be 
	used, not the first.
9. Circular macro invocation loops (never-ending loops) are not permitted
10. Circular function invocation loops (never-ending loops) are not permitted

Notes regarding the caveats

Note 01: The breach of any of the above caveats will cause the throwing
	of an error by the IX Software.
Note 02: A list of required functions will be provided at the end
	of the generated code, as comment statements.
Note 03: A list of all the entity definitions will be provided at the end
	of the generated code, as comment statements.
Note 04: The first macro code module definition (or two) will usually define 
	many of the entities that are invoked later in the program.
Note 05: Each entity can be assigned more than one value, but in every case 
	the previous value will be lost.
Note 06: A macro or a function can be invoked more than once.  In these
	cases, the function can be given different calling parameters,
	and a macro can use different entities.

This is the last of the changes to this article (as of 2024CMar15).

More recent information about the FRUN() function can be found in Article 217.

Unfinished Components of the IX Software System

Note that, as of 2024CMar13, the FRUN function has NOT yet been coded, but it is not very complicated. A initial version of the "requires()" function is still incomplete. The "hash total" and "text unpack" software will not be included in the early versions of the IX Software. However, the IX Software folder structure is complete (see the _KIOXIA_GTLL SSD) and the package naming convention has been finalized. The ISO9003 repository containing all of the author's software is taking shape, currently housing approximately 100 code modules. This gargantuan project is finally going into its last stretch.

Conclusion

A list of modules is often necessary to create an error-free large run-time python module for an application. The location of each module can be specified and is often a package of modules (eg pkg_py.py). The IXf.py program will be used to assemble all the modules that comprise an IX software program. The end result is a valid run-time python code module. The resulting code module is usually stored in the current folder (usually ../Desktop/a/sandBox") and finally processed by python and then executed. If the user wishes to move or copy the resulting code module elsewhere (possibly into a package or into a microprocessor such as a Raspberry Pico), the user must do this independantly of the IXf processor. Thonny or the IX textpack programs can be used to do this or Terminal commands can also be used. The author's ixRPC project (see Articles 193 and 196) is still in its infancy.

It should be noted that a text file that is a valid batch file for the IXf processor, if devoid of any macros or "requires() function" statements, can produce valid runnable python code. Such python code does not need to pass through the prepIX preprocessor because FRUN can generate any valid python functions. The prepIX program can be used to process any IXf batch file to convert it into a runnable python program. The prepIX program, if invoked, will expand any macros and will select (find and include) usable versions of functions in order to create the final runnable python program. However prepIX will only store the resulting code in the current folder. The prepIX program will not always run the resulting program. If prepIX is used instead of IXf, then the user must use python to actually run the resulting program. In this way prepIX (running on a Raspberry Pi) can often generate programs that will later be run using Thonny, Circuit Python or MicroPython instead of python. The prepIX program can also be used as a macro preprocessor to convert an "html" batch file into an "html" stub module. But html code cannot run directly on a Raspberry Pi under the Bookworm OS.

As of 2024CMar15, the full IXf system, including prepIX, is not complete, but it very nearly finished. The author of the IXf system has successfully run the macro preprocessor, an early version of prepIX, the hashtotal program and the textpack program. All that remains is to decide on the final format of an IXf batch program and do the final testing. The early versions of the IX Software system will only use an early version of prepIX called prepIX_I. However, to use the early version of IXf, the user must prepare a pair of functions for each function that will be used.

The author feels that dawn is finally breaking for the release of this IXf Software project. This project will be first tested on a Raspberry Pi with its operating system loaded onto an SSD drive that is connected via a USB-3 connector. Either RPi 4 or RPi 5 computers should be able to easily run the IXf Software. An SSD is not necessary, but uSD drives will definatly slow down the IXf software. As of 2024CMar13, all portions of the IX Software are copywritten by their author: D@CC and the ICH180RR company.

/IX_Macro_Processor.txt (This text row will soon be removed.)

Other Related Thoughts

CM4 Issues

Poor quality uSD cards will not boot up a CM4. For more info see the subsequent section with the heading: uSD Card Quality BootTime Issue .

Compute Module 4

(To enlarge .....Click it)
thumb: Raspberry-Pi-Introduces-New-Compute-Module-4.jpg
Raspberry Pi Compute Module 4


The author has recently (2023KNov02) purchased a Compute Module 4 with 8GM of Ram, 16GB of EMMC but no Wifi. It's datasheet can be seen in Source 02. A visual inspection of the CM4 does not find a silkscreen of the model identification which is CM4008016. The first "0" means no Wifi. Where can the model number be found on the actual device?

(To enlarge .....Click it)
thumb: CytronMakerCM4.jpg
Cytron CM4 Maker Board


 WARNING - Connecting the CM4 upside down (180 degrees) will cause severe damage.
         (Heed the silk-screened outlines on the Maker board.)
The size of the Maker board (shown above) is much larger than the WaveShare board (shown below). The size difference is best understood by comparing the true-size shape outline of the CM4 compute module on each board. Of course, Cytron wanted to have many LEDs, many external device connectors, a few switches, an audio jack, a buzzer and a coin-battery for the RTC on their board (above) while WaveShare wanted to make their board's dimensions resemble an actual Raspberry Pi board (but with an SSD drive underneath) as shown below. Note that the CM4 compute module is mounted front to back (between its connector pair) on both boards.

See Source 04 for information on the Cytron CM4 Maker board (cost US$ 51.52 [+ shipping] bare without a CM4). What really caught my eye was the M.2 Key-M connector for the Sabrent 2230 SSD drive (mentioned in Source 05). This connector is not SATA SSD compatible. Of course, this Cytron board also has a Gigabyte Ethernet connector for CM4 boards lacking WiFi. Unfortunately, the CM4 Maker board could not provide any USB-3 connectors. Absence of USB-3 seems to be a difficult shortcoming of the CM4 to correct. The CM4 Maker board does have a microSD connector socket for those CM4 Lite boards (that don't have any eMMC memory). It can be powered by a USB-C connector or a 7v to 18v DC jack. A power switch is present. A RUN (Boot) switch selects booting from a microSD card instead of from an SSD drive (or the eMMC). The author intends to soon purchase a Cytron CM4 Maker board. CM4 connectors exist for a CSI Camera Port, a DSI Display Port and a coin-battery for the RTC. The CM4 Maker board also has 5 Grove connectors, 1 Stemma QT Quiic "Maker port", 10 GPIO leds, 3 push buttons, a full-size HDMI connector and a piezo buzzer with a mute switch. Source 06 is a group of lessons (projects) to help a user learn about the CM4 Maker Board.

It is very interesting to see that many of these features have been incorporated into the RPi 5. This speaks well of both devices!

(To enlarge .....Click it)
thumb: WaveShare_23228_C.jpg
WaveShare 23228C Board (top view)

[It was attempted to display both images (imm. above and above that) to the same scale].

PiShop US sells the WaveShare 23228 C, CM4-IO-BASE-C [version C is shown above] for US$ 25.95 bare without the CM4. This version C board has many, but not all of the features of the Cytron CM4 Maker Board. This WaveShare version C board is an upgrade of the version A board. This WaveShare version C board also has a M.2 Slot with PCIe M.2 KEY-M connector for a MVME SSD 2230 drive etc (located underneath). The webmaster has recently (as of 2023KNov20) ordered the WaveShare version C board. A WaveShare board (version A) can be purchased from Cytron as a kit containing everything needed (even including the CM4 compute module) to function like a normal Raspberry Pi Board, but which is configurable and faster. It will be very interesting to compare the 3 year old CM4 compute module with the RPi 5 (recently released in the fall of 2023).

(To enlarge .....Click it)
thumb: IMG_0068.jpg
WaveShare CM4-IO-BASE-C-V1 Board (bottom view)

[Note the RTC jumpers (unchanged) and the SABRENT 2230 SSD].

The WaveShare CM4 board, that was on-order, arrived. The steps for setting up and starting the WaveShare CM4 board (Source 12) are:
-remove the film covering the SSD screw
-plug in a SABRENT 2230 SSD drive that has already been in use
-fasten the SSD using the supplied screw
-plug in the Compute Module 4 e.g CM4008016 (press very firmly in place)
-connect the HDMI cable
-connect the keyboard & mouse to the USB-2 connectors
-set the on-board BOOT switch OFF for SSD or uSD (ON use is unknown)
-connect the USB-C power cable
-optionally insert a CR1220 button cell.
In Source 13, about their CM4-IO-WIRELESS-BASE-B product (a different product), regarding its BOOT switch which may be applicable to the above WaveShare board, it says:
ON:  CM4 will be booted from USB-C interface
OFF: CM4 will be booted from eMMC or TF [Ed note: should probably be uSD] card
No changes to the config.txt file were necessary. No RTC, fan or camera were used. OS bookworm had already been installed on the SSD. The Compute Module 4 took about 30 seconds to power up the OS (that had been installed on the SSD drive.)

(To enlarge .....Click it)
thumb: RPiCM4_with_IOBoard.jpg
RPi CM4 with CM4 IOBoard

[Note: photo from datasheets.raspberrypi.com].

See the YouTube video (Video Source 01) by ShotokuTech describing how to start up a CM4 on an RPi CM4 IO Board. The video explains how to flash the firmware, but don't bother.

Using a CM4 with a SABRENT SSD (bookworm OS)

After the Raspberry Pi Compute Module IO board arrived. I started it up after doing the following:
-connect to USB-2 the SABRENT 2230 SSD drive that has already been in use
-do not connect a uSD card
-do not connect an Ethernet cable
-plug in the Compute Module 4 e.g CM4008016 Lite (press very firmly in place)
       Orient it correctly (with the CE logo nearest the edge of the board)
       If the CM4 has no WiFi (Lite) , no Internet Access will exist.
-connect the HDMI cable to HDMI0
-connect the keyboard & mouse through a USB hub to a USB-2 connector
-do not jumper the 2 leftmost pins of the J2 jumper block
       Label says "Fit jumper to disable eMMC boot"
-connect the 12v (with positive center pin) adapter
After about 8 seconds, the red power LED (on the IO board) will light up. The SSD blue light will blink. Then OS bookworm will be loaded from the SSD drive.

Using a CM4 w/o eMMC with uSD Fla99 (bullseye)
-connect the uSD card Fla99S032G
-do not connect an Ethernet cable
-do not connect the SABRENNT01 SSD drive
-plug in the Compute Module 4 e.g CM4102000 w/o eMMC (press very firmly in place)
       Orient it correctly (with the CE logo nearest the edge of the board)
       If the CM4 has no WiFi (Lite) , no Internet Access will exist.
-connect the HDMI cable to HDMI0 (not HDMI1.) HDMI1 will show no task menu on top.
-connect the keyboard & mouse through a USB hub to a USB-2 connector
-do not jumper the 2 leftmost pins of the J2 jumper block
       Label says "Fit jumper to disable eMMC boot"
-connect the 12v (with positive center pin) adapter
The CM4 will boot up the bullseye OS. The SABRENT01 SSD drive can now be plugged into a USB-2 connector, to access its contents.

Using a CM4 w/o eMMC to flash a uSD using the Ethernet bootloader

This procedure works well on a Raspberry Pi-400 but the CM4 needs updated firmware. This firmware update can be done by a user. If the firmware is NOT up-to-date, the steps and the results are:
-connect an empty uSD card
-connect an Ethernet cable
-do not connect the SABRENT01 SSD drive
-plug in the Compute Module 4 e.g CM4102000 w/o eMMC (press very firmly in place)
       Orient it correctly (with the CE logo nearest the edge of the board)
       If the CM4 has no WiFi (Lite) , only Ethernet access exists.
-connect the HDMI cable to HDMI0 (not HDMI1)
-connect the keyboard & mouse through a USB hub to a USB-2 connector
-do not jumper the 2 leftmost pins of the J2 jumper block
       Label says "Fit jumper to disable eMMC boot"
-connect the 12v (with positive center pin) adapter
The computer will display:
Raspberry Pi Compute Module 4 - 2GB
bootloader
. . .
link ready
. . .
Firmware not found
. . .

The above message means that more recent (more up-to-date) firmware must be loaded to the CM4. See Source 14, the datasheet for the RPi Compute Module 4 IO board. In it, Section 2.14 describes all the jumpers on jumper block J2.

uSD Card Quality BootTime Issue

Today, 2023LDec19, I encountered intermittent boot-ups when experiementing with different boot procedures on the various Compute Module 4 IO boards. Finally, I concluded that higher quality uSD cards such as the Samsung EVO cards almost always booted in every valid boot scenario. It seemed that the most "fragile" scenario was when the Compute Module 4 (CM4) was testing to see if the SD card was usable, before the CM4 decided to use the Ethernet bootloader. On the WaveShare CM4 IO board, the poor quality uSD cards were often not detected as being present, but the higher quality uSD cards were almost always detected, even when the uSD card was inserted in a USB uSD card reader. I had previously noticed that the poor quality uSD cards sometimes FAILED the "SD Card Speed Test" that has a binary PASS/FAIL result in the Raspberry Pi Diagnostics submenu. But the failing uSD cards almost always seemed to work satisfactorily for data read/writes. The CM4 IO boards clearly needed higher quality uSD cards. In my most recent tests, the higher quality uSD cards used the "bookworm" OS, while the lower quality uSD cards used the older "bullseye" OS. I doubt that the OS made much difference to this boot-time uSD card issue. This issue seems more prevalent on the Compute Module 4 computers compared to the "normal" RPi computers.

This uSD Card Quality issue makes me even more pleased with my recent decision to use an SSD drive instead of uSD cards when working with Raspberry Pi computers. I recently purchased a 256 GB SSD drive for about US$ 20. This cost is less than double the cost of a high-quality 32GB uSD card. Furthermore SSD drives are much faster than uSD cards. Anyone who spends hours a day on a computer shouldn't risk the loss of many hours of work due to a bad quality uSD card.

Sources

Video Sources

Video Source V209:01: Raspberry Pi Compute Module 4 Get Started Step by Step (5:40m) by ShotokuTech on 2021 E May 03

Web Sources

Web Source S209:01:www requires_v01_py.txt by D@CC on 2023JOct26
Web Source S209:02:www RPi Compute Module 4 DataSheet by Raspberry Pi c2023
Web Source S209:03:www RPi Compute Module 4 Review by Jeff Geerling on 2020JOct19
Web Source S209:04:www [Cytron] CM4 Maker Board by Cytron on 2023CMar08
Web Source S209:05:www Search for Sabrent 2230 in Article 206 by D@CC on 2023ISep25
Web Source S209:06:www . . . CM4 Maker Board [Lessons] by Abdul Salam A Haris at Cytron on 2023CMar08
Web Source S209:07:www ix_pkg_py.txt D@CC on 2023KNov03
Web Source S209:08:www ISO9003_Versions.txt D@CC on 2023KNov03
Web Source S209:09:www IX: IX Software Family: requires( function. . .) II (210.html) D@CC on 2023KNov07
Web Source S209:10:www IT: prepIX_I (196.html) D@CC on 2023EMay16
Web Source S209:11:www Pi: Creating Python Packages of Functions (174.html) D@CC on 2022BFeb26
Web Source S209:12:www WaveShare CM4-IO-BASE-C WaveShare after 2021JOct30
Web Source S209:13:www WaveShare CM4-IO-WIRELESS-BASE-B WaveShare as of 2023LDec19
Web Source S209:14:www DataSheet: Raspberry Pi Compute Module 4 IO Board by Raspberry as of 2023LDec19
Web Source S209:15: www IX bash utilities and IX macros (217.html) by D@CC as of 2024CMar19

/SourcesEnd


There is a way to "google" any of the part-numbers, words or phrases in all my articles. This "google-like" search limits itself ONLY to my articles. Just go to the top of "Articles by Old King Cole" and look for the "search" input box named "freefind".

Click here to return to Articles by Old King Cole

Date Written :2023 K Nov 02
Last Updated:2024 C Mar 19

All rights reserved 2023 by © ICH180RR

saved in E:\E\2022\DevE\MyPagesE\Globat\ePhotoCaption.com\a\209\209.html
backed up to ePhotoCaption.com\a\209\209_2023KNov28.html

Font: Courier New 10 (monospaced)

/209.html