Skip to main content

A webdev framework that enables development of full-stack webpages and app purely in Python

Project description

Ofjustpy

Python versions Tests Status Coverage Status Flake8 Status

Documentation PyPI Downloads Downloads per week GitHub stars

Ofjustpy: A Full-Stack Web Development Framework in Python

Features:

  • Single language: Ofjustpy allows you to build full-featured web pages using only the Python programming language. You don't need to learn and manage multiple languages like Jinja2 templating, JavaScript, HTML, and CSS. The entire functionality of a webpage can be expressed using pure Python.
  1. Emphasis on API Design: Much emphasis has been put on API design and programmatic constructs in order to be able to build complex responsive webpages and websites with codebase that easy to read and maintain.

  2. Scalable and efficient: Ofjustpy enables programmers to write scalable and efficient webpage code. It maintains only a single copy of static components, regardless of the number of connections opened. Data copying per connection and per event is minized. Even for mutable components, only a single copy of the component's static portion is maintained. Additionally, communication is efficient as it transmits only the changes (diff) to the UI to the frontend.

  3. Composibility: Ofjustpy allows users to easily build reusable components and incorporate them into their codebase. See htmlcomponents.py and ofjustpy-components for simple to complex components build using Ofjustpy framework.

  4. Tailwind CSS Support: Ofjustpy provides first-class and native support for Tailwind CSS constructs, enabling complex manipulation (add, remove, update classes) of the frontend UI through pure Python expressions.

  5. Async Processing: Like its predecessor Justpy, Ofjustpy utilizes an asynchronous webserver stack that includes Starlette, ASGI, asyncio, and async/await. This architecture enables Ofjustpy to efficiently handle concurrent connections, even on single-core hardware or virtual environments, without the need for OS-level multi-threading.

  6. Technology Stack: The tech stack of ofjustpy consists of Svelte (as the frontend UI engine), websockets (for communication between browser and server), Starlette (the async webserver), amd Tailwind (for layout and design).

Gallery

Webpage programming using Ofjustpy

The basic paradigm for building out webpages in Ofjustpy is straighforward:

  1. Create components and containers
  2. Describe their layout and appearance using tailwind constructs
  3. For components that will respond to events on UI, describe handlers (actions) for those events

If you are new to web development, then it would be helpful to go through the justpy docs and its examples on which Ofjustpy is based on. Also, pay careful attention to known corner cases and suggested workarounds mentioned in Gotchas to keep Ofjutpy engine happy

The demo example illustrates the three paradigms for building out webpages:

import ofjustpy as oj
from py_tailwind_utils import *


labels = [oj.Mutable.Label(text="mytext1", key="mylabel1"),
          oj.Mutable.Label(text="mytext2", key="mylabel2"),
          oj.Mutable.Label(text="mytext3", key="mylabel3"),
          oj.Mutable.Label(text="mytext4", key="mylabel4"),
          ]

mydeck = oj.Mutable.StackD(key="mydeck",
                           childs=labels,
                           height_anchor_key="mylabel1",
                           
                           twsty_tags=[W/"1/2"])

idx = 1
def on_btn_click(dbref, msg, target_of):
    global idx
    mydeck_shell = target_of(mydeck)
    mydeck_shell.bring_to_front(labels[idx].id)
    idx = (idx + 1)%4
    pass

        
mybtn = oj.AC.Button(key="mybtn",
                   text="abtn",
                   pcp=[W/32, H/32, bg/rose/6],
                   on_click=on_btn_click
                   )
wp_endpoint = oj.create_endpoint(key="static-components",
                 childs = [mydeck,
                           mybtn
                           ]
                 )        
oj.add_jproute("/", wp_endpoint)

Components and their types in Ofjustpy

Ofjustpy, boradly, defines 2 kinds of components: static vs mutable. Static components are further classified into Passive and Active, while mutable components come in three variety: HCCMutable, HCCStatic, Mutable.

Passive Components

As name suggests, these do not respond to UI events and also do not undergo any changes. Ofjustpy maintains only a single copy of such components (see (see examples/static_webpages) Examples of passive components:

  1. oj.PC.Label
  2. oj.PC.P
  3. oj.PC.Img

etc. See SHC_types.py for list of all passive components.

Active Components

Active components handle events but like passive do not mutate. Active components require an additional key/id argument (see examples/input_components). Examples of active components:

  1. oj.AC.Button
  2. oj.AC.TextInput
  3. oj.AC.CheckboxInput
  4. oj.AC.Select
  5. oj.AC.A

and so on. See SHC_types.py for list of all active components

Mutable components

Mutable components can handle events and also mutate, i.e., change appearances. See examples/mutable_webpages for usage. Examples of mutable components include:

  1. oj.Mutable.Button
  2. oj.Mutable.Label
  3. oj.Mutable.Container and so on. See MHC_types.py for list of all mutable components.

HCCMutable Components

These are div components that contain mutable childrens. The div itself is not mutable (see examples/mutable_webpages/example_004.py). HCCMutable components offer minor space optimization over Mutable divs. Examples of HCCMutable components:

  1. oj.HCCMutable.Div
  2. oj.HCCMutable.StackV
  3. oj.HCCMutable.StackW
  4. oj.HCCMutable.Subsection See MHC_types.py for list of all HCCMutable components.

HCCStatic Components

These are div components that itself are mutable but they contain static (passive or active) components. Examples of HCCStatic components:

  1. oj.HCCStatic.Div
  2. oj.HCCStatic.StackV

See MHC_types.py for list of all HCCStatic components. jj

Tailwind-derived higher-order components

In addition to standard HTML components (like, Div, Span, Button, etc.), Ofjustpy offers advanced components that offer complex functionality. These are build using Tailwind CSS constructs, Svelte, and Python.

  1. Containers to juxtapose child components StackH, StackV, StackG, StackW : Stack components horizontally, vertically, grid, and wrap around. See examples examples/static_webpages/example_001.py,
    examples/static_webpages/example_002.py for StackV, examples/static_webpages/example_004.py for StackG, examples/static_webpages/example_002.py for StackW.

  2. StackD: Deck the items on top of each other. See examples examples/mutable_webpages/example_005.py, examples/mutable_webpages/example_008.py

  3. Slider A component with discreet value buttons to select a value. See examples examples/mutable_webpages/example_002.py, examples/mutable_webpages/example_003.py, examples/mutable_webpages/example_006.py.

  4. ColorSelector Component create select drop down option for all tailwind preset color values. See example examples/mutable_webpages/example_007.py

See ofjustpy-components github repo for collection of advanced components.

The Ofjustpy paradigms: app module, TwStyCtx, MountCtx, PageBuilderCtx

App module

Ofjustpy requires that starlette app be defined in its own file lets say "theapp.py". Then set the OJ_APP_MODULE to set the location of the theapp.py relative to directory from where the webserver is being launched.

export OJ_APP_MODULE="../the_app"

A sample example of theapp.py looks as follows:

from asgi_signing_middleware import SerializedSignedCookieMiddleware

import ofjustpy as oj
csrf_middleware = Middleware(CSRFMiddleware,
                                secret=csrf_secret,
                                field_name = csrf_cookie_name)

signed_cookie_middleware = Middleware(SerializedSignedCookieMiddleware,
                               secret=b'a very, very secret thing',  
                               state_attribute_name='messages',  
                               cookie_name='my_cookie',
                               cookie_ttl=60 * 5,  
                               )


auth_middleware =     Middleware(AuthenticationMiddleware,
                                 backend=BasicAuthBackend(),
                                 on_error=lambda _, exc: PlainTextResponse("error during authentication", status_code=401)
                                 )



app =  oj.load_app([auth_middleware,
                    signed_cookie_middleware,
                    csrf_middleware
                    ])

User defined default sty module and TwSty context

The default UI style of a component is determined by its type. For each component type (e.g., Button, Span), a style is defined in the snowsty.py module, which resides within the src/ofjustpy directory. You can customize the default style by registering a style file with Ofjustpy. To load your custom style, set the OJSTY environment variable accordingly. Please note that the default style for a component can be overridden using the twsty_tags directive. You can also modify the style programmatically throu TwStyCtx context expression. Ofjustpy provides two style definitions snowsty and un. The un style does not add any styling to the components.

An example illustrate style switching:

with oj.TwStyCtx("un"):
    oj.Button(..., twsty_tags = []) # Draw a with un default styling 
oj.Button(..., twsty_tags = []) # use dnowsty as default styling

Customizing Default Styling in Ofjustpy

In Ofjustpy, the default UI style for a component is determined by its type. Each component type, such as Button or Span, has a predefined style located in the snowsty.py module within the src/ofjustpy directory.

To personalize the default styling, you can register a custom style file with Ofjustpy. To make use of your custom style, simply set the OJSTY environment variable accordingly.

Please bear in mind that you have the flexibility to override the default style for a component using the twsty_tags directive.

In addition to setting OJSTY, you can programmatically modify the style using the TwStyCtx context expression.

Ofjustpy offers two predefined style definitions: snowsty and un. The un style, in particular, refrains from adding any additional styling to the components.

Here's an example to illustrate style switching:

with oj.TwStyCtx("un"):
    oj.Button(..., twsty_tags=[])  # Draws with the 'un' default styling
oj.Button(..., twsty_tags=[])  # Uses 'snowsty' as the default styling

By following these guidelines, you can easily tailor the default styles in Ofjustpy to suit your project's aesthetic preferences.

MountContext

The MountContext construct simplifies the process of mounting all the routes defined in a module. Consider the following example:

with oj.MountCtx("examples"):
   with oj.MountCtx("static_webpages"):    
       static_webpage_module = importlib.import_module("examples.static_webpages",
                                                            )
															

In the above example, all the routes defined within the examples.static_webpages will be mounted under examples/static_webpages path. MountCtx enable a plug-and-play approach for adding modules and defining routes. This means you can import the module into multiple web app codes without needing to modify the module code for each app. This flexibility simplifies the process of extending and reusing modules across different applications.

PageBuilder Context

The PageBuilderCtx context is used to modify or post-process webpage endpoints defined within a module. Let's consider the following example:

def page_builder(edir):
    """
    We need edir to find the example directory within which the example is located.
    
    """
    def page_builder_inner(key, childs, **kwargs):
        title = kwargs.get('title')
        nav_buttons = //define a list of navigation buttons
        

        return oj.Mutable.WebPage(key=key,
                               childs = [oj.HCCMutable.StackV(childs = [oj.HCCMutable.Div(childs=childs),
                                                                oj.PC.Hr(twsty_tags=[bg/green/1]),
                                                                nav_buttons],
                                                      twsty_tags= [space/y/8]
                                                      )
                                         ],

                               **kwargs
                               )

    return page_builder_inner



with oj.PageBuilderCtx(page_builder("mutable_webpages")):
    with oj.MountCtx("input_components"):
        input_components_module = importlib.import_module("examples.input_components",
                                                            )
															

In the above example, the oj.PageBuilderCtx context is used to post-process all endpoints defined within the examples.input_components module. Specifically, it allows for the addition of navigation buttons at the bottom of each page generated by the endpoints in this module. This structured approach simplifies the task of modifying and enhancing the behavior of web pages associated with the input_components module by applying a consistent post-processing step to all of them.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ofjustpy-1.1.2.tar.gz (106.4 kB view details)

Uploaded Source

Built Distribution

ofjustpy-1.1.2-py3-none-any.whl (37.4 kB view details)

Uploaded Python 3

File details

Details for the file ofjustpy-1.1.2.tar.gz.

File metadata

  • Download URL: ofjustpy-1.1.2.tar.gz
  • Upload date:
  • Size: 106.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-requests/2.30.0

File hashes

Hashes for ofjustpy-1.1.2.tar.gz
Algorithm Hash digest
SHA256 83eaa729ee912d6b387a84897bd2288df97c2e31e1073e7b05d6c4bbf0b9701d
MD5 70e13d5a6c47dd20ca9b94bcf525e242
BLAKE2b-256 759177b5c182d9349e168870c4aaca17bab60d6f9c101ee38bf6596ac232f00c

See more details on using hashes here.

File details

Details for the file ofjustpy-1.1.2-py3-none-any.whl.

File metadata

  • Download URL: ofjustpy-1.1.2-py3-none-any.whl
  • Upload date:
  • Size: 37.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-requests/2.30.0

File hashes

Hashes for ofjustpy-1.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 a6cf5ef0ca0ea0d5ee781fb50ab2434b6444a336c05220039978b7aef4bcc616
MD5 36851b8d32056f3b0f5505517ba43d61
BLAKE2b-256 499fc261d55dd8d4a886d623c58f1883901d667825773b045e9fe08f529f7f9d

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page