Skip to main content

Package for extending the Calmjs framework to support the usage of sass in a manner that crosses Python package boundaries by exposing an import system that mimics the package namespaces available within a given Python environment.

Project description

calmjs.sassy

Package for extending the Calmjs framework to support the declaration and usage of sass in a manner that crosses Python package boundaries by exposing an import system that mimics the package namespaces available within the current Python environment, such that styling rules can be more easily propagated to their dependants. This facilitates the reuse of styling rules declared by Python packages’ dependencies in a manner more familiar to users of the given Python environments by reusing the same names as the namespaces they may be using.

https://travis-ci.org/calmjs/calmjs.sassy.svg?branch=1.0.2 https://ci.appveyor.com/api/projects/status/1gei512socwe8nho/branch/1.0.2?svg=true https://coveralls.io/repos/github/calmjs/calmjs.sassy/badge.svg?branch=1.0.2

Introduction

While the calmjs framework can support the production of deployable artifacts for web applications (through packages such as calmjs.rjs and calmjs.webpack), the styling of the application would be incomplete without exposing the relevant stylesheets to dependant packages.

With the usage of the extensibility of the calmjs framework, a registry dedicated for sass may be declared. Tools that make use of these declarations may also be integrated to generate a single (or a set of) stylesheets for use with the library or the application.

Features

This package provides:

  • A base registry that finds all .scss files declared in a Python package.

  • A basic toolchain for linking all the .scss files exported by a given Python package(s) and their dependencies, plus optionally their Node.js/npm dependencies discovered through the dependency graph with the aid of calmjs, for the generation of .css files for use by the application or export to other libraries. A specific implementation that links against libsass-python is provided.

  • A calmjs runtime that makes use of the libsass-python toolchain for end-user one-off CSS generation.

Installation

To install calmjs.sassy into a given Python environment, the base package may be installed directly from PyPI with the following command:

$ pip install calmjs.sassy

If support for the usage of libsass-python is desired, the installation command will be the following:

$ pip install calmjs.sassy[libsass]

If this package is used as part of the build process, and the default libsass-python toolchain is used for CSS artifact generation, the dependency may be declared like so in the package’s setup.py file:

setup(
    ...
    setup_requires=[
        'calmjs.sassy[libsass]>=1.0.0,<2',
        # plus other packages required for generating the package.
    ],
    install_requires=[
        # actual dependencies required for the usage of the package.
    ],
)

Alternative installation methods (advanced users)

Development is still ongoing with calmjs.sassy; the development version may be installed through git like so:

$ pip install calmjs
$ pip install git+https://github.com/calmjs/calmjs.sassy.git#egg=calmjs.sassy

Alternatively, the git repository can be cloned directly and execute pip install -e . while inside the root of the source directory.

Newer versions of pip and setuptools may omit the initial manual installation of the calmjs package.

If setup.py within the calmjs.sassy source tree is used directly, please keep in mind that calmjs MUST be available before that is executed, so that all the required package metadata may be generated correctly. Alternatively, please execute python setup.py egg_info if any message about Unknown distribution option: is noted during the invocation of setup.py.

As calmjs is declared as both namespace and package, there are certain low-level setup that is required on the working Python environment to ensure that all modules within can be located correctly. However, versions of setuptools earlier than v31.0.0 does not create the required package namespace declarations when a package is installed using this development installation method when mixed with pip install within the same namespace. As a result, inconsistent import failures can happen for any modules under the calmjs namespace. As an example:

>>> import calmjs.sassy
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named 'calmjs.sassy'
>>> import calmjs.base
>>> import calmjs.sassy
>>>

If this behavior (and workaround) is undesirable, please ensure the installation of all calmjs related packages follow the same method (i.e. either python setup.py develop for all packages, or using the wheels acquired through pip), or upgrade setuptools to version 31 or greater and reinstall all affected packages.

Testing the installation

Finally, to verify for the successful installation of calmjs.sassy, the included tests may be executed through this command:

$ python -m unittest calmjs.sassy.tests.make_suite

Usage

To generate the .css artifact for given package(s) that have exported .scss styles through the Calmjs module registry system, and that the package libsass-python is available, the following command may be executed:

$ calmjs scss example.package

The following sections will provide an overview on how this export system may be enabled for Python packages. For a more detailed explanation on how the module registry works or how Calmjs works in general, please refer to the README provided by the calmjs package, under the section Export JavaScript code from Python packages.

The default runtime also exposes a number of tuneable features as flags that are documented below; the specifics may be found by running calmjs scss --help.

Declaring SCSS files to export for a given Python package

SCSS files embedded within a Python package can be exposed to the calmjs.scss registry which is provided by this package. For example, given the the following entry points for that registry defined by a package named example:

[calmjs.scss]
example = example

This is the most basic declaration that works for packages that share the same name as the import location provided.

The following is am example for packages that have nested submodules (called example.lib and example.app):

[calmjs.scss]
example.lib = example.lib
example.app = example.app

While the import locations declared looks exactly like a Python module (as per the rules of a Python entry point), the calmjs.scss registry will present them using the CommonJS/ES6 style import paths (i.e. 'example/lib' and 'example/app'), so users that need those style rules need to be ensure that they import those strings.

Please also note that the default source extractor will extract all *.scss files within those directories.

Putting the second example together, the setup.py file should contain the following:

setup(
    name='example',
    setup_requires=[
        'calmjs.scss',
        # plus other setup level requirements
    ],
    # the entry points are required to allow calmjs to pick this up
    entry_points="""
    [calmjs.scss]
    example.lib = example.lib
    example.app = example.app
    """,
)

Ensure the SCSS is structured in the supported manner for reuse

For proper generation of the resulting .css and the management of the .scss usage and exports, the default libsass-python toolchain imposes a small number of fixed constraints when default settings are used. The main constraint is that a specific entry point file must be declared to be used to acquire the styling rules for the specified packages for the generation of the stylesheet artifact(s). This parameter typically defaults to index.scss, but this may be specified to a different value to generate different styling rules, for example for usage with different application end points.

In essence, this allow the package to create an artifact with just the explicit imports and styling rules defined within it, while exporting the rules it defines to their dependants piecemeal so that they may be able to import them using the similar package namespace and module names. This enables general communication of exports and reusability of those styling rules without forcing dependants to explicitly declare their required styles multiple times (i.e. only the @import statement is needed in the stylesheet itself, and no need to declare an explicit entry against something outside the package).

For example, inside an example.package there may be this layout:

.
├── example
│   ├── __init__.py
│   └── package
│       ├── __init__.py
│       ├── colors.scss
│       ├── content.py
│       ├── form.py
│       ├── form.scss
│       ├── index.scss
│       ├── ui.py
│       ├── ui.scss
│       └── widget.js
└── setup.py

The entry point declaration to export the .scss files within the example package will be this:

[calmjs.scss]
example.package = example.package

Note that the index.scss (the default entry point name) for this package may contain the following:

@import "example/package/colors";
@import "example/package/form";
@import "example/package/ui";

Which are simply imports of all the .scss modules provided by the package itself. For an example.dependant package that depends on example.package and exports their own styling rules, it will need to declare its dependency through the install_requires keyword in its setup.py and declare the following entry point to expose the styles defined:

[calmjs.scss]
example.dependant = example.dependant

Putting it all together:

setup(
    name='example.dependant',
    install_requires=[
        'example.package',
        # ... plus other dependencies
    ],
    entry_points="""
    [calmjs.scss]
    example.dependant = example.dependant
    """,
    # ... plus other declarations
)

Note that the entry specific to its dependency example.package is already declared already by that package. For the main entry point index.scss of example.dependant, it may contain the following:

@import "example/dependant/colors";
@import "example/dependant/full_ui";
@import "example/package/form";

In this example, only the form.scss styles exported by the example.package was included, while omitting colors.scss and ui.scss as it could clash with the definitions required and implemented by the other styles it shipped in that dependant package (e.g. colors and full_ui). Other dependants of this example.dependant package may then declare usage of any of these exported styles as per their owners’ preferences. This is one method to provide extensible styles that are reusable in a piecemeal manner by package dependants.

Naturally, there are parameters to specify entry points other than index.scss for a given package, if necessary (for example, multiple stylesheets may need to be exported for use with different workflows provided by the given package).

Include .scss files in Node.js package repositories

As the integration with Node.js was the goal of the Calmjs framework, it is possible to declare linkage with Node.js packages that ship with .scss files from package repositories such as npm. The actual usage is very similar to the typical integration through Calmjs, where the difference lies in the keywords to be specified.

For example, a setup.py may contain the following:

setup(
    name="example.package"
    package_json={
        "dependencies": {
            "bootstrap": "~4.0.0",
        },
    },
    extras_calmjs_scss={
        'node_modules': {
            'bootstrap': 'bootstrap/scss',
        }
    },
)

The declaration above with simply expose all the .scss files inside the bootstrap package from npm as the directory was declared to be used for the build process. Importing the desired module from that dependency is simply:

@import "bootstrap/nav";
@import "bootstrap/navbar";

Would work seamlessly, much like the usage of JavaScript code.

Complete artifacts from npm may also be explicitly specified to export under a specific identifier.

Declaring prebuilt, standard CSS bundle for the Python package

Finally, to complete the Python package deployment story, the process should include the automatic generation and inclusion of the stylesheet artifacts in the resulting Python wheel. This can be achieved by specifying an entry in the calmjs.artifacts registry, with the key being the filename of the artifact and the value being the import location to a builder. A default builder function provided at calmjs.sassy.artifact:complete_css will enable the generation of a complete stylesheet, based on the default toolchain and settings, with calmjs.sassy.artifact:complete_compressed_css provide a spec that will produced compressed style output. Note that both these builders make use of the libsass-python toolchain.

An example entry point configuration that only produce the complete css artifact (without compression):

[calmjs.artifacts]
example.bundle.css = calmjs.sassy.artifact:complete_css

Once those entry points are installed, running calmjs artifact build example.package will make use of the SCSS toolchain and build the artifact at example.bundle.css inside the calmjs_artifacts directory within the metadata directory for example.package. Alternatively, for solution more integrated with setuptools, the setup function in setup.py should also enable the build_calmjs_artifacts flag such that setup.py build will also trigger the building process. This is useful for automatically generating and including the artifact as part of the wheel building process.

A more complete definition that generates both form of the artifacts may look like the following setup.py:

setup(
    name='example.package',
    # to enable calmjs artifact generation integration w/ setuptools
    build_calmjs_artifacts=True,
    entry_points="""
    # ... other entry points truncated
    [calmjs.module]
    example.package = example.package

    [calmjs.artifacts]
    example.bundle.css = calmjs.sassy.artifact:complete_css
    example.bundle.min.css = calmjs.sassy.artifact:complete_compressed_css
    """,
    # ... other required fields truncated
)

Building the wheel using setup.py may result in something like this.

$ python setup.py bdist_wheel
automatically picked registries ['calmjs.scss'] for sourcepaths
...
invoking 'sass.compile' on entry point module at '/tmp/tmpwb5bhmd0/build/__calmjs_sassy__/index.scss'
wrote export css file at '/home/user/example.package/src/example.package.egg-info/calmjs_artifacts/example.bundle.css'
installing to build/bdist.linux-x86_64/wheel
...

With both example.bundle.css and example.bundle.min.css available under the calmjs_artifacts sub-directory inside the package metadata directory inside the Python wheel that was generated.

Also note that the default builder specifies index as the default entry point. If other ones need to be provided or other options are required, simply create a new builder function that return a Spec object with the desired values.

The end result is that end-users of this package will be able to make use of the complete features provided without having to go through a separate build step, while retaining the ability for regenerating all the required artifacts with just the build dependencies installed, without having to further acquire the original configuration files (or even the source files) from the original repository that are required for the production of these artifacts as they are part of the package (provided that the original sources are also packaged into the wheel).

Using registered .scss with other Calmjs Node.js/JavaScript toolchains

While the registry system is designed to be as extensible and reuseable as much as possible, those specific underlying tools may expect a different complete module name as they may require specific loader string prepended to the stylesheet. This topic will require more exploration for better overall integration, despite the building blocks to acheive this is available in the base/generic form.

Troubleshooting

UserWarning: Unknown distribution option:

During setup and installation using the development method, if this warning message is shown, please ensure the egg metadata is correctly generated by running python setup.py egg_info in the source directory, as the package calmjs was not available when the setup script was initially executed.

CalmjsSassyRuntimeError: missing required package ‘libsass’

Please install the libsass-python package; this can be achieved by running:

$ pip install libsass

CalmjsSassyRuntimeError: failed to compile with libsass

This can be caused by syntax errors within the styling rules, which should be reported as part of the output. Generation using the built-in calmjs scss runtime may return more detailed debugging information using the relevant flags as documented by the --help flag.

Internal Error: Data context created with empty source string

While using the libsass interfacing runtime or toolchain (e.g. the default calmjs scss runtime), if the provided package does not include (export) a .scss file for the specified entry point name (the default is index, and so the package must export a index.scss file at the location registered in the calmjs.scss registry), nothing will be imported thus the source string will be empty, resulting in libsass aborting the execution.

Contribute

Changelog

1.0.2 (2020-04-23)

  • Use self.opener for writing file manipulation inside toolchain.

1.0.1 (2018-05-23)

  • Minor text fixes.

1.0.0 (2018-05-23)

  • Initial release of the Sassy CSS integration for Calmjs.

  • Provide a base calmjs.scss registry to allow Python packages to export .scss files for their dependants to utilize.

  • Provide a base calmjs scss runtime to interface with the default libsass-python toolchain for the production of .css artifacts for any given Python packages.

  • Provide a couple artifact builders that integrates with the calmjs artifact production framework.

Download files

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

Source Distribution

calmjs.sassy-1.0.2.zip (49.4 kB view hashes)

Uploaded Source

Built Distribution

calmjs.sassy-1.0.2-py2.py3-none-any.whl (31.5 kB view hashes)

Uploaded Python 2 Python 3

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