top of page
Search
  • Akmod

POP: A Deep Dive into the Unique Conventions of POP Projects

In the dynamic landscape of Python development, Plugin Oriented Programming (POP) offers a breath of fresh air. Built on an architecture that maximizes modularity and code reusability, POP is revolutionizing Python projects. In this article, let's peel back the layers of POP’s unique conventions, including the hub, subs, LoadedMods, and dynamic namespaces.

The Hub: The Nucleus of POP

In POP, the hub acts as the central namespace, analogous to the root directory ("/") in file systems. Just like how file systems have directories and sub-directories that can be nested and merged, the hub holds various namespaces that are organized hierarchically. Essentially, it acts as a global 'self' variable that is implicitly passed to every function within the POP project, serving as a central repository for functions and variables.

Subs and LoadedMods: The Building Blocks

Within the hub, there are specialized namespaces known as 'Subs'. A 'Sub' can contain other 'Subs' as well as 'LoadedMods'. A LoadedMod is essentially a Python file that has been assimilated into the hub. This integration replaces traditional import statements as POP encourages direct interaction between the hub and your code through namespaces.

Dynamic Namespaces: The Customizable Containers

Dynamic namespaces allow you to extend the hub with custom subs. This is done in the conf.py file (located in project_root/pop_source/conf.py) by creating a dictionary as shown below:

DYNE = {"dynamic_subsystem_name": ["dir_1", "dir_2"]}

This maps dynamic namespaces to directories that contain code extending these namespaces.

Initializing the Hub

To kickstart the hub, initiate it, add dynamic namespaces, and then load subdirectories as shown below:

import pop.hub
hub = pop.hub.Hub()
hub.pop.sub.add(dyne_name="dynamic_subsystem_name")
hub.pop.sub.load_subdirs(hub.dynamic_subsystem_name, recurse=True)

Now, your dynamic namespaces are part of the hub and ready for action.

Organization Matters

Organization and structure play a crucial role in POP. The directory path and structure implicitly dictate how elements are referenced within the hub. Being deliberate with naming and structure, with an eye for clarity and descriptiveness, can make your POP project intuitive and easy to navigate.

Let’s consider an example. Say you have a file located at:

project_root/pop_source/dynamic_subsystem_name/folder1/folder2/my_script.py

This file can be accessed through the hub using the following reference:

hub.dynamic_subsystem_name.folder1.folder2.my_script

This reference is derived directly from the directory structure, where dynamic_subsystem_name, folder1, and folder2 are subs, and my_script is a LoadedMod.

Adding a Variable to the Hub

Variables can be defined within a LoadedMod and are directly accessible through the hub. Let's say you want to define a global variable in my_script.py:

# my_script.py
MY_GLOBAL_VARIABLE = "This is a global variable"

You can access this variable via the hub:

hub.dynamic_subsystem_name.folder1.folder2.my_script.MY_GLOBAL_VARIABLE

By convention, global variables are defined in ALL CAPS.

Adding a Function to the Hub

Likewise, functions defined within a LoadedMod can also be accessed via the hub. For example, in my_script.py:

# my_script.py
def greet(hub, name):
    return f"Hello, {name}!"

This function can be invoked through the hub as follows:

message = hub.dynamic_subsystem_name.folder1.folder2.my_script.greet(name="Alice")
print(message)
# Output: Hello, Alice!

Being mindful of how you structure your directories and files, as well as naming conventions, will make your POP projects more maintainable and collaborative. Remember to stick with snake_case for function names, directory names, and file names -- it's just the right thing to do.

LoadedMods: Special Attributes

LoadedMods come with a few special attributes that give them superpowers in POP:

  1. __init__: This function is akin to a constructor, executing when the file is added to the hub. It is conventionally used to define the structure of the project. For instance: def __init__(hub): # typically used for structural definitions ...

  2. __virtual__: This function determines whether the LoadedMod should be added to the hub. If it returns True, the LoadedMod is added; if it returns False, it’s excluded. This is incredibly handy for conditional loading based on specific environments or criteria. For example: def __virtual__(hub): # Load this LoadedMod only on Windows return True if os.name == 'nt' else False

  3. __func_alias__: This attribute allows you to define aliases for functions. This is particularly useful when there’s a need to avoid naming conflicts with Python keywords. For instance: __func_alias__ = {"list_": "list"} def list_(hub, *args, **kwargs): ... In this example, __func_alias__ ensures that the function list_ is added to the hub under the alias list. This circumvents the clash with the built-in Python list keyword.

These attributes enable you to have a granular control over how your LoadedMods behave and integrate with the hub.


Classes? Not in POP

POP eliminates the need for classes, polymorphism, or inheritance. Instead, everything becomes plugins on the hub. Classes are just for types. This architecture promotes namespaces, where functions and variables are organized under subs and LoadedMods.

Conclusion

Plugin Oriented Programming is a powerful paradigm that brings modular design to the forefront. Through the central concept of the hub, coupled with subs and LoadedMods, POP provides an elegant, organized, and scalable way to structure Python projects. As you embark on your journey through POP, remember that like well-maintained code, every namespace should have a purpose, and every function should do one thing and do it well.

39 views0 comments

Commenti


bottom of page