Intermake
=========
___APPLICATION FRONTEND VIA REFLECTION___

* Please go to the [usage tutorial](#Usage-tutorial) section for how to **use** an Iɴᴛᴇʀᴍᴀᴋᴇ application
* Please go to the [development tutorial](#Development-tutorial) section for how to **create** an Iɴᴛᴇʀᴍᴀᴋᴇ application

[](toc)

What is Intermake?
------------------

Iɴᴛᴇʀᴍᴀᴋᴇ is a library that provides a front-end to Pʏᴛʜᴏɴ functions.
The following frontends are supported:

* `ARG`: Call from **command line arguments**
* `CLI`: Interactive terminal-like shell (**CLI**)
* `PYI`: Interactive **Pʏᴛʜᴏɴ shell** (PYI)
* `GUI`: Graphical user interface (**GUI**)
* `PYS`: Call from any **Pʏᴛʜᴏɴ script** or application
* `???`: **Custom** front-ends are also supported


### Rationale ###

Unlike other utilities, Iɴᴛᴇʀᴍᴀᴋᴇ:
* Requires minimal setup – an `import intermake` and the `@command` decorator
* Isn't intrusive
* Generates help and documentation _automatically_ – using the [PEP-257](https://www.python.org/dev/peps/pep-0257/) documentation you probably already use
* Generates an _appropriate_ UI _automatically_:
    * Uses [PEP-484](https://www.python.org/dev/peps/pep-0484/) annotations
    * Converts information provided by the user _automatically_:
        * A function, requiring an `int`, receives an `int`, not the `str` the user typed
        * A function, requiring a `MyOwnClass`, receives a `MyOwnClass`
    * Supports annotation hints:
        * Function defaults
        * The `typing` library: `List[T]`, `Union[T, U]`, `Optional[T]`, etc.
        * Includes custom-annotations: `Filename[extension]` (str), `Dirname` (str), `Nonzero[T]` (T)
* Abstracts common concepts – scroll bars, feedback to user, questions to user, etc.
* Abstracts threading to the host



Usage tutorial
--------------

Where `sample` is the name of your application, use the following Bash commands to launch it:

| Mode                           | Command                                          |
|--------------------------------|--------------------------------------------------|
| Command-line arguments         | `sample "<commands>"`                            |
| Command line interface         | `sample` <br/> ...or... <br/> `bio42 "ui cli"`   |
| Graphical user interface       | `sample "ui gui"`                                |
| Pʏᴛʜᴏɴ interactive shell       | `sample "ui pyi"`                                |
| Interface for Pʏᴛʜᴏɴ scripting | `python` <br/> ...then... <br/> `import sample`  |

Iɴᴛᴇʀᴍᴀᴋᴇ provides an ***extensive, context-dependent, in-line help*** system.

Let's explore the CLI mode. First, launch your application from the command line in the default (CLI) mode.

```bash
$   sample
```

Once inside `sample`, use the `help` command to get started.

```bash
$   help
    ECO help
    INF   _help____________________________________________
          Aliases: basic_help
    
                        You are in command-line mode.
    
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
```

Basic usage
-----------

### CLI ###

Iɴᴛᴇʀᴍᴀᴋᴇ application supports a Command Line Interactive UI, with a Paup-like interface.
To start this, from your terminal just type:

```bash
intermake
```

Where `intermake` is replaced by the name of your application.

Once in CLI mode, just type commands to run them.
e.g. to see the list of the application's commands type:

```
cmdlist
```

Or to run the "eggs" command type:

```
eggs
```
    
(You can abbreviate all commands, so you can also just type `egg`)

You can use `?` to get help on a command:

```
eggs?
```
    
(You can also type `?eggs` or `help eggs`)

See that "eggs" takes two arguments, "name" and "good".

You can specify the arguments after the command:

```
eggs Humpty True
```

You can also name the arguments:

```
eggs good=True
```

You can also use `?` to get help on the last argument:

```
eggs name?
```

Specify `+` to quickly set boolean arguments:

```
eggs +good
```
    
(you can abbreviate all arguments, so you can just use `eggs +g`)

To pass multiple commands on the same line use ` : ` (surrounded with spaces)

```
eggs Tweedledum : eggs Tweedledee
``` 

You need to use quotes to pass parameters with spaces:

```
eggs "Humpty Dumpty"
```

### PYI ###

Iɴᴛᴇʀᴍᴀᴋᴇ apps can be run from a Python Interactive session (PYI mode).
To start your application in PYI mode, from your terminal just type

```
intermake pyi
```

Where `intermake` is replaced by the name of your application.
Once in PYI mode the interface behaves like regular Python.
The application's commands have already been imported into the global namespace. 
e.g. to see the list of the application's commands type:

```
cmdlist()
```

Or, to run the `eggs` command:

```
eggs()
```

All commands are Python objects, so you can use `.help()` for help:

```
eggs.help()
```

See that `eggs` takes two arguments, `name` and `good`.

You can specify arguments like so:

```
eggs("Humpty", True)
```

Or you can name them:

```
eggs(name = "Humpty", good = True)
```
    
For help on Python itself, invoke the standard Python help via:

```
python_help()
```

### PYS ###
Iɴᴛᴇʀᴍᴀᴋᴇ apps can be run from a Python script (PYS mode).

Usage is the same as for any other Python library, simply import the application's namespace.
e.g. where `intermake` is the name of your application:

```python
import intermake
```

You can then run commands as for the PYI mode, e.g. to run the `cmdlist` command:

```python
intermake.cmdlist()
```

Or to run the `eggs` command:

```
intermake.eggs()
```

All commands are Python objects, so you can use `.help()` for help:

```
intermake.eggs.help()
```

See that `eggs` takes two arguments, `name` and `good`.

You can specify arguments like so:

```
eggs("Humpty", True)
```

Or you can name them:

```
eggs(name = "Humpty", good = True)
```
    
For more help on Python itself, invoke the standard Python help via:

```
help()
```

You can start the user interfaces via their respective commands, e.g. to start the CLI run:

```
intermake.cli()
```

For extending an Iɴᴛᴇʀᴍᴀᴋᴇ application and providing your own commands, please see the [development tutorial](#Development-tutorial). 

### ARG ###

Iɴᴛᴇʀᴍᴀᴋᴇ apps can be run directly from the command line (ARG mode)
For instance, where `intermake` is the name of the application, from your terminal just type:

```
intermake commands ...
```
    
For instance, to see the list of commands type:

```bash
intermake cmdlist
```
    
Or to run the `eggs` command:

```bash
intermake eggs
```
    
(all commands can be abbreviated so you can also use `intermake egg`)

You can use `?` to get help on a command:

```bash
intermake eggs?
```
    
(you can also use `intermake ?eggs` or `intermake help eggs`)

See that "eggs" takes two arguments, "name" and "good".

You can specify the arguments after the command:

```bash
intermake eggs Humpty True
```
    
If it's complex, you can put your command in quotes so as not to confuse your terminal:

```bash
intermake "eggs Humpty True"
```

You can name the arguments:

```bash
intermake eggs good=True
```

You can also use `?` to get help on the last argument:

```bash
intermake eggs name?
```
    
This works for quick reference, if you are busy typing a command:

```bash
intermake eggs name=Humpty good?
```

Specify `+` to quickly set boolean arguments:

```bash
intermake eggs +good
```
    
(you can abbreviate all arguments, so you can just use `intermake eggs +g`)

You should use more quotes to pass parameters with spaces:

```bash
intermake eggs "Humpty Dumpty"
```
    
For the list of commands type:

```bash
intermake cmdlist
```
    
To start the UI:

```bash
intermake cli
```
    
Use ` : ` (surrounded with spaces) to pass multiple commands

```bash
intermake cmdlist : cli
```
    
You can substitute ` : ` for ` then `, if `:` confuses your command line interpreter.

```bash
intermake cmdlist then cli
```

You can also surround your entire command string with quotes, if this makes things easier:

```bash
intermake "cmdlist : cli"
```

If you don't specify any commands, the CLI UI will start automatically.

### GUI ###

All Iɴᴛᴇʀᴍᴀᴋᴇ applications can also be run in a graphical user interface (GUI) mode.
To start this mode from your terminal type:

```
intermake gui
```

Where `intermake` is the name of your application.

A window will then appear showing you the available commands. 

Advanced usage
--------------

### Starting the application ###

Please see the [Usage Tutorial](#Usage-tutorial) above. 

### Running a command ###

Please run the `help` command for information on how to run commands and pass arguments to them.
This will give you help specific to the particular mode you are running (CLI/GUI/PYI/PYS).

* Examples:
    * CLI: `help`
    * PYS: `import intermake; intermake.help()`
    * PYI: `help()`
    * ARG: `my_application help`
    * GUI: Navigate to `plugins/common/help` and click `run`.

### Changing the main settings ###

Use the `set` command to view and modify settings.

* Example:
    * CLI: `set console/error_traceback=True`
* Notes:
    * GUI: This command is an `advanced` command and is hidden by default. Please see the [listing special commands](#Listing-special-commands) section for information on how to enable these commands.


### Listing commands ###

Use the `cmdlist` command to list the most common commands.

* Example:
    * `cmdlist`
* Notes: 
    * GUI: The GUI lists all commands in the main window, so you don't need to do this.


### Listing special commands ###

Advanced commands include lesser used Iɴᴛᴇʀᴍᴀᴋᴇ commands, as well as commands specific to the application.
To show them you can use the `use` command.

* Example:
    * CLI: `use advanced`
* Notes:
    * ARG/CLI/PYI/PYS: These modes still allow you to run commands regardless of whether they are hidden or not, however typing `cmdlist` will now show the `advanced` commands.
    * GUI: From the GUI you will be unable to use the advanced commands without showing them first.


### Configuring the workspace directory ###

Iɴᴛᴇʀᴍᴀᴋᴇ stores its data in a "workspace" this folder is by default `~/.intermake-data/application-name`, however you can change this by using the `workspace` command.

* Example:
    * CLI: `workspace c:\my\folder`
* Notes:
    * GUI: This command is an `advanced` command and is hidden by default. Please see the [listing special commands](#Listing-special-commands) section for information on how to enable these commands.


### Using compute clusters ###

Use the `compute_cluster` command to configure parallel jobs. This option is only shown by default if there is at least one command supporting a parallel workload.

* Example:
    * CLI: `compute.cluster 7 9

### Switching the user interface ###

Use the `ui` command.

* Example:
    * CLI: `ui gui`


### Exploring object hierarchies ###

Use the `cd` and `ls` commands.

* Example:
    * CLI: `cd ../results`
* Notes:
    * These commands are only shown by default if the application defines an object hierarchy to explore. 
    * GUI: The GUI shows the object hierarchies in the main window, so you do not need to use these commands.
    




Development tutorial
--------------------

### Implementation ###

Get right in there with the full implementation of a simple Iɴᴛᴇʀᴍᴀᴋᴇ application, called Sᴀᴍᴩʟᴇ! It has two amazing functions "`say_hello`" and "`do_some_work`".
Paste the following contents into the appropriate files:

[`sample/sample/__init__.py`]
```python
from intermake import MCMD, MENV, command

@command()
def say_hello():
    """
    Says hello
    """
    MCMD.print( "hello" )

@command()
def do_some_work( count : int ):
    """
    Does nothing.
    
    :param count: The number of times to do nothing.
    """
    with MCMD.action( "Doing some work for you!", count ) as action:
        for n in range( count ):
            action.increment()

MENV.name = "sample"
```

[`sample/sample/__main__.py`]:
```python
import sample
import intermake

if __name__ == "__main__":
    intermake.start()
```

**_That's all there is!_** The next section describes what we actually did!

### Explanation ###

1. We used the `@command()` decorator to expose our function through Iɴᴛᴇʀᴍᴀᴋᴇ.
2. We properly documented our functions using PEP-287 doc comments.
3. We properly annotated our function parameters using PEP-484.
4. We used the `MCMD` field to obtain the abstraction to the current UI.
    * We called `MCMD.print` to print a message.
    * We called `MCMD.action` to show a progress bar.
5. We set the application name `MENV.name`.
    * (Iɴᴛᴇʀᴍᴀᴋᴇ doesn't impose any branding, so if we don't do this our application just gets called "`Untitled`".)
6. Finally, we called `intermake.start` to start Iɴᴛᴇʀᴍᴀᴋᴇ with the appropriate UI.

### Running our application ###

This is Pʏᴛʜᴏɴ boilerplate stuff, but if you don't already know:
* Do a quick-and-dirty registration with Pʏᴛʜᴏɴ by running:

```bash
export PYTHONPATH=$PYTHONPATH:/path/to/sample`.
alias sample="python3 -m sample"
```

Now you can try out the various modes as follows. They all do the same thing, in different ways.

#### CLI mode ####

```bash
BASH   <<< sample
SAMPLE <<< say_hello
       <<< do_some_work count=10000
       <<< exit
```

#### ARG mode ####

```bash
BASH   <<< sample "say_hello : do_some_work count=10000"
```

#### GUI mode ####

```bash
BASH   <<< sample "ui gui"
SAMPLE <<< *click say_hello*
       <<< *click run*
       <<< *click do_some_work*
       <<< *set count to 10000*
       <<< *click run*
       <<< *close the window*
```

#### PYI mode ####

```bash
BASH   <<< sample "ui pyi"
PYTHON <<< say_hello()
       <<< do_some_work(10000)
```

#### PYS mode ####

```python
PYTHON <<< import sample
       <<< sample.say_hello()
       <<< sample.do_some_work(10000)
```

Adding support for new types
----------------------------

* Iɴᴛᴇʀᴍᴀᴋᴇ uses Eᴅɪᴛᴏʀɪᴜᴍ to supply the Qᴛ GUI editor. To add GUI support for new types, call `editorium.register` (see the `editorium` module itself for details).
* Iɴᴛᴇʀᴍᴀᴋᴇ uses SᴛʀɪɴɢCᴏᴇʀᴄɪᴏɴ to supply the CLI translations from text. To add support for translation from text to new types, call `stringcoercion.register` (see the `stringcoercion` module itself for details).  
    
Advanced plugins
----------------

In our example we provided a _plugin_ through the `@command` decorator.
`@command` also allows us to register *classes*, if those classes are subclasses of the `Plugin` type.
The `plugins` directory of `intermake` contains the set of in-built plugins, which may be used as a reference-point for creating your own.

***Please see the doc-comments on `@command` and `Plugin` for full details.***

Customising
-----------

The example above set only the application title, more customisations are available via the `MENV` field, notably the `root` field, that exposes an application hierarchy derived from `IVisualisable`:

```python
MENV.root = my_root
```

Once a root is set, the user can navigate your hierarchy using UNIX/DOS like commands:

```bash
$   sample
$   cd plugins
```

The `MENV` field is a `__Environment` object defined in `environment.py`.

***Please see the doc-comments on `__Environment` and `IVisualisable` for full details.***

System requirements
-------------------

* Iɴᴛᴇʀᴍᴀᴋᴇ uses type annotations, which require at least Pʏᴛʜᴏɴ 3.6.
    * This must be installed first. 

* Supported platforms
    * Windows 7,8
        * Iɴᴛᴇʀᴍᴀᴋᴇ uses Cᴏʟᴏʀᴀᴍᴀ to add support for ANSI-escape sequences
        * Iɴᴛᴇʀᴍᴀᴋᴇ instructs the user on how to enable UTF-8
    * Windows 10
    * Ubuntu
    * MacOS
        * Beware that this ships with a legacy version of Pʏᴛʜᴏɴ 2 by default, you'll need to update!

* Terminal environment requisites (`CLI`/`ARG`/`PYI`/`PYS`)
    * ANSI escape sequences
        * _If not supported_: Weird characters in console output
    * ANSI colours (optional)
        * _If not supported_: No colours will be shown
    * UTF8 (optional)
        * _If not supported_: Weird/missing data in console output.
            * Note: Pʏᴛʜᴏɴ itself must still be notified via the `PYTHONIOENCODING` environment variable, e.g. `export PYTHONIOENCODING=ascii:replace`. If this is not done the application will crash (Iɴᴛᴇʀᴍᴀᴋᴇ supplements the Python error with a more helpful error description).
    * _readline_
        * _If not supported_: Up/down will not work to invoke command history in console. 

* Graphical user interface requisites (`GUI`)
    * _PyQt5_.
        * Note: at the time of writing, some versions of Ubuntu ship with a broken _Qt_/_PyQt5_/_Sip_ install, giving a `killed 9` or `segfault` error on GUI startup. This will require re-installation of _Qt_/_PyQt_/_Sip_ by the user.
        * _If not supported_: Cannot start GUI. Iɴᴛᴇʀᴍᴀᴋᴇ's UI components are isolated so the console modes should still work fine providing the application itself also isolates its GUI (which it should).
    
Troubleshooting
---------------

### Generally weird errors ###

User errors:

* Iɴᴛᴇʀᴍᴀᴋᴇ requires at least Pʏᴛʜᴏɴ 3.6.
    * (This is especially problematic on Mac, which for some reason ships with a legacy version of Pʏᴛʜᴏɴ 2) 


### Unicode errors ###

User errors:

* You're using Pʏᴛʜᴏɴ2, Iɴᴛᴇʀᴍᴀᴋᴇ requires at least Pʏᴛʜᴏɴ3.6.
* You've changed the terminal encoding.
    * Check the solutions for Ubuntu and Windows below, regardless of your platform.

On Ubuntu:

* Problem: The terminal is using an implicit encoding `LC_ALL=C`. Pʏᴛʜᴏɴ can't handle this.
    * Solution - Use UTF8. Call `export LC_ALL="en_GB.utf8"` from the command line.
        * (Replace the `en_GB` (UK) with your own locale, e.g. `es.utf8` for Spain or `zh_CN.utf8` for China.)

On Windows:

* Problem: `cmd.exe` or _PowerShell_ with an `ASCII`-only font.
    * Solution - Change your font to a Unicode one.
    * Quick workaround - call `set PYTHONIOENCODING=ascii:replace` from the command line
    
On Mac:

* No known problems unless you've reconfigured your terminal, see "User errors" above.


### Segmentation fault, killed 9, or GUI fails to run ###

On Ubuntu:

* Problem: At the time of writing, some Linux systems have a corrupt installation of PyQt and/or Qt.
    * Solution: Build PyQt and/or Qt properly from source yourself, see: 
        * [riverbankcomputing.com](https://riverbankcomputing.com/software/pyqt/intro)
        * [qt.io](https://www.qt.io/)
    * Workaround: Just use your Iɴᴛᴇʀᴍᴀᴋᴇ application from one of the CLI modes.
    
On Windows or Mac:

* No known problems

On other systems (e.g. Android):

* Problem: Qt is not supported.
    * Solution: See if you can find a Qt installation for your system.
    * Workaround: Use your Iɴᴛᴇʀᴍᴀᴋᴇ application from one of the CLI modes.
    
    
### An Iɴᴛᴇʀᴍᴀᴋᴇ application doesn't start for some other reason ###

User errors:

* Problem: Requisite libraries not installed
    * Solution: Please follow the installation instructions included with the application, generally your software should have been installed via `pip`, here are some common errors:
        * You are using `pip` for Pʏᴛʜᴏɴ2 to try and install a Pʏᴛʜᴏɴ3 application.
        * You are running `pip` without sufficient privileges:
            * Use `sudo` (UNIX)
            * Use the administrator shell (Windows)
            * Use `virtualenv`
            
    
### General errors ###

Coding errors:

* Problem: A Iɴᴛᴇʀᴍᴀᴋᴇ error occurs.
    * Solution: Report it so it can be fixed. Please include the debug information in your bug report. 
        * GUI: Iɴᴛᴇʀᴍᴀᴋᴇ writes debugging information to the console (`stderr`) whenever it encounters an error in the GUI.
          Launch the GUI from the command line (e.g. `./xxxx "ui gui"`) to see this information.
        * CLI: If an error occurs in the CLI version, details can be retrieved using the `error` command.
            * You can instruct all errors to be dumped by default via the `set` command.
    * Solution: Fix it yourself! All Iɴᴛᴇʀᴍᴀᴋᴇ code is heavily documented in-line using standard Pʏᴛʜᴏɴ documentation. 
    
    
### Hard to read text in CLI mode ###

Iɴᴛᴇʀᴍᴀᴋᴇ does its best to use colours to distinguish on-screen items, and expects standard ANSI/DOS colours.
If you're getting unreadable items, such as yellow text on a white background, the colours should be changed in your terminal preferences (not the Iɴᴛᴇʀᴍᴀᴋᴇ application itself). Modify your terminal profile/theme or simply turn off ANSI colour support.


### Locked environment ###

Intermake runs as a singleton instance, that is, you cannot define two Intermake applications using the same piece of Python code.
More than one application simply doesn't make sense: which application would the user interact with?
Assuming just one instance simplifies a lot of things, namely, you don't need to specify the application every time you define a plugin.

Normally, you can only set `MENV.name` once. If you try to change the name more than once, an error is raised.
This way, if you accidentally import another `Intermake` application into the current one, you'll get an error, rather than ending up with some weird chimerical hybrid of both applications.

That said, applications can build on top of each other, providing increased levels of functionality.

* To add plugins to an existing application:
    * You can add plugins to an existing application without problem, simply import the existing application's module, then use `@command` to register your own plugins.
* To extend an existing application, replacing the name, version, root object, etc.
    * Import the existing application's module, then call `MENV.unlock` to let Intermake know the name change is not a problem.
* To provide a new application, that can also extend an existing application. 
    * Check `MENV.is_locked`. If this is `True`, another Intermake application is already setup, proceed to add plugins as required but don't redefine the Intermake environment.

If you get a locked environment error, you've tried to `import` another Intermake application module, and that application is trying to overwrite the current one. This might be because:
* You've using `import` on an unrelated Intermake application.
* You're trying to `unpickle` data from that application.
* You've not checked `MENV.is_locked`.
* You've not called `MENV.unlock`.


Meta
----

```ini
type        = library
author      = Martin Rusilowicz
language    = python3
created     = 2017
host        = bitbucket,pypi
```
