Skip to main content

Micro Components for Python

Lightweight library to create components that can automagically be used as either CLI or as native classes in other programming languages

pip pip

The same package is available for Node. Check it out if you want Python and Node components talking to each other!

Goals & Design

Often times, which programming language I pick is predetermined by technical requirements and the resources available in that language. In many cases I have to use a mixture of nodejs, Python, and bash scripts, and they all have to talk to each other.

The intent of this library is to provide a utility class turning Singleton classes into command line interfaces and providing an intuitive class interface between different programming languages. Since a lot of the code I produce is either Node or Python, I started by creating components for those two languages.

The beauty of this is that components have a simple JSON interface to talk to each other, irregardless of the programming language. Any other language implementing the Component interface can now be imported as if it were a native class.

Installation

In your terminal run:

$ pip install micro-components-py

Usage

To run a component from the terminal, you need the following structure at minimum:

from micro_components import Component

class TaskRunner(Component):
	name = 'task_runner'

	def run(task_id):
		return { 'task_id': task_id, 'status': 'running' }

TaskRunner.export_as_cli();

To then be able to run one of the following commands from your terminal:

$ taskrunner.py run 95
# output: { "task_id": 95, "status": "running"  }
$ taskrunner.py run --task-id="hello world"
# output: { "task_id": "hello world", "status": "running"  }
$ taskrunner.py run --task-id="[5,9,true]"
# output: { "task_id": [5, 9, true], "status": "running"  }

Example of Python and Node components interacting:

Consider this JavaScript component called task_logger.js:

const fs = require('fs');
const { Component } = require('micro-components-js');

const TaskLogger = Component({
	name: 'task_logger',

	log(infos) {
		fs.writeFileSync('log.json', infos);
	}
});

TaskLogger.export_to_cli();

We can now use TaskLogger from within our task_runner.py code:

from micro_components import Component
TaskLogger = Component.from_cli('./task_logger.js')

class TaskRunner(Component):
	name = 'task_runner'

	run(task_id):
		result = { 'task_id': task_id, 'status': 'running' }
		TaskLogger.log(result)
		return result

TaskRunner.export_as_cli()

Creation from Terminal

You can use the micro-components CLI to create a component like so:

In your terminal:

$ micro-components create "My Component"

This will create a skeleton component file in your current directory.

Naming Convention

Components have a snake_cased filename and hold the same name as a property at minimum:

from micro-components import Component

class SomeComponent(Component):
	name = 'some_component'

Class names follow usual conventions of StartCase.

from components.intent_matcher import IntentMatcher

Exporting

You can use one of two export methods to either import components as modules "inline" or call their methods from the command line.

As CLI

To turn a component into a CLI, use the command export_as_cli():

SomeComponent.export_as_cli()

Manual Creation

If you create your component by hand, make sure your file is executable by adding a Shebang at the top of it…

#!/usr/bin/env python3

…and by giving it execution permissions (in your terminal)…

$ chmod +x ./components/some_component.py

…to then run the methods:

$ ./components/some_component.py fetch_data "parameter" 15 "{ \"sub-param\": \"value\" }"

The Component class will automagically look at the parameter defaults of your component's methods and try to parse parameters passed through the CLI accordingly.

Consider the following example component:

class RecipeFetcher(Component):
	name = 'recipe_fetcher'
	counts = 0

	def get_ingredients(ingredient_name, max_count=10, normalize=True, options={}):
		...

RecipeFetcher.export_as_cli();

We pass the following arguments via CLI:

$ ./components/recipe_fetcher.py get_ingredients "Onion Soup" 15 false "{ \"pepper\": false }"

The component class will automatically parse 15, false, and the passed JSON string as JSON and use the true datatypes.

Named Arguments

You can pass named arguments by prefixing the parameter names with two hyphens instead:

$ /components/recipe_fetcher.py get_ingredients --ingredient_name "Mangosteen" --normalize=1

Properties for the component class can be passed the same way. Arguments that don't match method names will be applied as properties. Any hyphens will be replaced by underscores, meaning --user-id will be read as user_id.

$ /components/recipe_fetcher.py get_ingredients --user-id=1337 --ingredient_name="Capers"

Method Chaining

Methods can be chained using the chain notation (beware that spacing is crucial):

$ /components/recipe_fetcher.py [ load_ingredients "all" , get_ingredients "Pepper" 12 ]

will first run RecipeFetcher.load_ingredients("all"), then RecipeFetcher.get_ingredients("Pepper", 12) and return a dictionary of results for each execution.

Caching

Before using any of the built-in caching functionality, make sure you have a directory called data/caches to store those in.

TODO: add documentation for caching decorators

A JSON cache will be auto-generated in /data/caches/<component_name>.cache.json.

API

Components provide the following interface:

Events

Components and utility classes all emit events that can be listened to. You can attach event listeners using the on method.

RecipeFetcher.on('spawn', lambda self: print('RecipeFetcher loaded.') or self)

The following events are available on every component:

Event Name Triggered … passed arguments
spawn once on initialization self instance, needs to be returned back
init every time the component is being updated using ComponentName.init(...) self instance, options object
cache every time a method's cache is being hit key used to retrieve value from cache
call_from_cli once upon calling the component from the command line command is the called method, args the passed arguments, verbose to turn console output on/off

To trigger (e.g. your own) events, use the trigger method:

RecipeFetcher.trigger('recipe_outdated', ['some', 'arguments'])

If the event handler returned something, it will be passed through trigger as return value.

new_recipe = RecipeFetcher.trigger('recipe_outdated', [old_recipe])

Properties

You can access non-callable properties from the CLI using the component module's built-in get and set methods in your terminal:

$ ./components/recipe_fetcher.py get counts 	# returns 1
$ ./components/recipe_fetcher.py set counts 3

Shortcuts

All components automatically get a help method, so if uncertain about properties run ./components/recipe_fetcher.py help. All methods automatically get a --help directive, so if uncertain about parameters run ./components/recipe_fetcher.py get_ingredients --help.

Integration

You can call components written in Javascript from Python and vice-versa as if they were written in the same language using the Component module.

Our previous RecipeFetcher example was written in Javascript, but now we want to use it in Python. Here's how:

from micro_components import Component

RecipeFetcher = Component.from_cli('./components/recipe_fetcher.js');
ingredients = RecipeFetcher.get_ingredients("Onion Soup", 15, False, { 'pepper': False })

To access properties, use the built-in property getters and setters (see "Properties"), like so:

from micro_components import Component

RecipeFetcher = Component.from_cli('./components/recipe_fetcher.py')
counts = RecipeFetcher.get("counts")

Testing

This package comes with a set of standard unittest cases located in [tests/test_micro-components.py][]

Run them using:

$ pytest

or

$ python3 ./tests/test_micro-components.py

Examples

Checkout the components folder in the repo for some examples. The CLI used to create component templates for example is a component itself.

License

MIT License

Metadata

Release files for micro-components-py 0.2.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for micro-components-py 0.2.3
File Size Uploaded Provenance Actions
micro_components_py-0.2.3.tar.gz 15.5 kB Without provenance Details for micro_components_py-0.2.3.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for micro-components-py 0.2.3
File Interpreter ABI Platform Actions
micro_components_py-0.2.3-py3.7.egg Legacy Egg format - - Details for micro_components_py-0.2.3-py3.7.egg
micro_components_py-0.2.3-py3-none-any.whl Python 3 none any Details for micro_components_py-0.2.3-py3-none-any.whl

Total release size: 63.0 kB

Release files / micro_components_py-0.2.3.tar.gz

Download URL micro_components_py-0.2.3.tar.gz
Size 15.5 kB
Tags Source
SHA-256 checksum
How to use checksums
323064645a692d12b22ff6d635ee817b316b35f7bc09c2b4a531abc3d08a08e9
BLAKE2b-256 checksum
How to use checksums
371de555004e6eccf7142154a88d995b6cbbd6984d28c49ed4c855fc95d39de5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.2.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/50.3.2 requests-toolbelt/0.9.1 tqdm/4.42.1 CPython/3.7.6

Release files / micro_components_py-0.2.3-py3.7.egg

Download URL micro_components_py-0.2.3-py3.7.egg
Size 32.0 kB
Tags Egg
SHA-256 checksum
How to use checksums
d4d4aa7b3be35716b6f0f9598da925a922f21eebf5c4a6e8a5bfef77e635fcbe
BLAKE2b-256 checksum
How to use checksums
74bdb2cbfe5358268e7ba0b8acc518828b9599a02d8a8ad83d4721605f063965
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.2.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/50.3.2 requests-toolbelt/0.9.1 tqdm/4.42.1 CPython/3.7.6

Release files / micro_components_py-0.2.3-py3-none-any.whl

Download URL micro_components_py-0.2.3-py3-none-any.whl
Size 15.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
537c1e1b50b9140b2b3eb4ee4ef2fb7e1ba47417c68b1f280f9025aa8bbe8496
BLAKE2b-256 checksum
How to use checksums
03d0b1a4cb42fb601932536ce541072c55733f0c3fe7230d1f5f2373f7014ac0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.2.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/50.3.2 requests-toolbelt/0.9.1 tqdm/4.42.1 CPython/3.7.6

Security for micro-components-py 0.2.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page