Package: calmjs.sassy
calmjs.sassyPackage for extending theCalmjs frameworkto support the declaration
and usage ofsassin a manner that crosses Python package boundaries
by exposing animportsystem 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.IntroductionWhile thecalmjsframework can support the production of deployable
artifacts for web applications (through packages such ascalmjs.rjsandcalmjs.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 forsassmay 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.FeaturesThis package provides:A base registry that finds all.scssfiles declared in a Python
package.A basic toolchain for linking all the.scssfiles 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 ofcalmjs, for the generation of.cssfiles for use by
the application or export to other libraries.  A specific
implementation that links againstlibsass-pythonis provided.A calmjs runtime that makes use of thelibsass-pythontoolchain for
end-user one-off CSS generation.InstallationTo installcalmjs.sassyinto a given Python environment, the base
package may be installed directly from PyPI with the following command:$pipinstallcalmjs.sassyIf support for the usage oflibsass-pythonis desired, the
installation command will be the following:$pipinstallcalmjs.sassy[libsass]If this package is used as part of the build process, and the defaultlibsass-pythontoolchain is used for CSS artifact generation, the
dependency may be declared like so in the package’ssetup.pyfile: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 withcalmjs.sassy; the development
version may be installed through git like so:$pipinstallcalmjs$pipinstallgit+https://github.com/calmjs/calmjs.sassy.git#egg=calmjs.sassyAlternatively, the git repository can be cloned directly and executepip install-e.while inside the root of the source directory.Newer versions ofpipandsetuptoolsmay omit the initial manual
installation of thecalmjspackage.Ifsetup.pywithin thecalmjs.sassysource tree is used directly,
please keep in mind thatcalmjsMUST be available before that is
executed, so that all the required package metadata may be generated
correctly.  Alternatively, please executepython setup.py egg_infoif any message aboutUnknown distribution option:is noted during
the invocation ofsetup.py.Ascalmjsis 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 ofsetuptoolsearlier thanv31.0.0does not create the
required package namespace declarations when a package is installed
using this development installation method when mixed withpip installwithin the same namespace.  As a result, inconsistent import
failures can happen for any modules under thecalmjsnamespace.  As an
example:>>>importcalmjs.sassyTraceback(mostrecentcalllast):File"<stdin>",line1,in<module>ImportError:Nomodulenamed'calmjs.sassy'>>>importcalmjs.base>>>importcalmjs.sassy>>>If this behavior (and workaround) is undesirable, please ensure the
installation of allcalmjsrelated packages follow the same method
(i.e. eitherpython setup.py developfor all packages, or using the
wheels acquired throughpip), or upgradesetuptoolsto version
31 or greater and reinstall all affected packages.Testing the installationFinally, to verify for the successful installation ofcalmjs.sassy,
the included tests may be executed through this command:$python-munittestcalmjs.sassy.tests.make_suiteUsageTo generate the.cssartifact for given package(s) that have
exported.scssstyles through the Calmjs module registry system, and
that the packagelibsass-pythonis available, the following command
may be executed:$calmjsscssexample.packageThe 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 thecalmjspackage,
under the sectionExport 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 runningcalmjs scss--help.Declaring SCSS files to export for a given Python packageSCSS files embedded within a Python package can be exposed to thecalmjs.scssregistry which is provided by this package.  For
example, given the the following entry points for that registry defined
by a package namedexample:[calmjs.scss]example=exampleThis 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
(calledexample.libandexample.app):[calmjs.scss]example.lib=example.libexample.app=example.appWhile the import locations declared looks exactly like a Python module
(as per the rules of a Python entry point), thecalmjs.scssregistry 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 theyimportthose strings.Please also note that the default source extractor will extract all*.scssfiles within those directories.Putting the second example together, thesetup.pyfile 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 upentry_points="""
    [calmjs.scss]
    example.lib = example.lib
    example.app = example.app
    """,)Ensure the SCSS is structured in the supported manner for reuseFor proper generation of the resulting.cssand the management of
the.scssusage and exports, the defaultlibsass-pythontoolchain
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 toindex.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@importstatement is needed in the stylesheet itself, and no need to declare
an explicit entry against something outside the package).For example, inside anexample.packagethere 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.pyThe entry point declaration to export the.scssfiles within the
example package will be this:[calmjs.scss]example.package=example.packageNote that theindex.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.scssmodules provided by the
package itself.  For anexample.dependantpackage that depends onexample.packageand exports their own styling rules, it will need
to declare its dependency through theinstall_requireskeyword in
itssetup.pyand declare the following entry point to expose the
styles defined:[calmjs.scss]example.dependant=example.dependantPutting 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 dependencyexample.packageis
already declared already by that package.  For the main entry pointindex.scssofexample.dependant, it may contain the following:@import"example/dependant/colors";@import"example/dependant/full_ui";@import"example/package/form";In this example, only theform.scssstyles exported by theexample.packagewas included, while omittingcolors.scssandui.scssas it could clash with the definitions required and
implemented by the other styles it shipped in that dependant package
(e.g.colorsandfull_ui).  Other dependants of thisexample.dependantpackage 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 thanindex.scssfor 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 repositoriesAs 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.scssfiles from package repositories such asnpm.  The actual
usage is very similar to the typical integration through Calmjs, where
the difference lies in the keywords to be specified.For example, asetup.pymay 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.scssfiles inside
thebootstrappackage fromnpmas 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 fromnpmmay also be explicitly specified to
export under a specific identifier.Declaring prebuilt, standard CSS bundle for the Python packageFinally, 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 thecalmjs.artifactsregistry, with the key
being the filename of the artifact and the value being the import
location to a builder.  A default builder function provided atcalmjs.sassy.artifact:complete_csswill enable the generation
of a complete stylesheet, based on the default toolchain and settings,
withcalmjs.sassy.artifact:complete_compressed_cssprovide a spec
that will produced compressed style output.  Note that both these
builders make use of thelibsass-pythontoolchain.An example entry point configuration that only produce the complete css
artifact (without compression):[calmjs.artifacts]example.bundle.css=calmjs.sassy.artifact:complete_cssOnce those entry points are installed, runningcalmjs artifact build example.packagewill make use of the SCSS toolchain and build the
artifact atexample.bundle.cssinside thecalmjs_artifactsdirectory within the metadata directory forexample.package.
Alternatively, for solution more integrated withsetuptools, thesetupfunction insetup.pyshould also enable thebuild_calmjs_artifactsflag such thatsetup.py buildwill 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 followingsetup.py:setup(name='example.package',# to enable calmjs artifact generation integration w/ setuptoolsbuild_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 usingsetup.pymay 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 bothexample.bundle.cssandexample.bundle.min.cssavailable under thecalmjs_artifactssub-directory inside the
package metadata directory inside the Python wheel that was generated.Also note that the default builder specifiesindexas the default
entry point.  If other ones need to be provided or other options are
required, simply create a new builder function that return aSpecobject 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 toolchainsWhile 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.TroubleshootingUserWarning: 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 runningpython setup.py egg_infoin the source
directory, as the packagecalmjswas not available when the setup
script was initially executed.CalmjsSassyRuntimeError: missing required package ‘libsass’Please install thelibsass-pythonpackage; this can be achieved by
running:$pipinstalllibsassCalmjsSassyRuntimeError: failed to compile with libsassThis can be caused by syntax errors within the styling rules, which
should be reported as part of the output.  Generation using the built-incalmjs scssruntime may return more detailed debugging information
using the relevant flags as documented by the--helpflag.Internal Error: Data context created with empty source stringWhile using the libsass interfacing runtime or toolchain (e.g. the
defaultcalmjs scssruntime), if the provided package does not
include (export) a.scssfile for the specified entry point name
(the default isindex, and so the package must export aindex.scssfile at the location registered in thecalmjs.scssregistry), nothing will be imported thus the source string will be
empty, resulting inlibsassaborting the execution.ContributeIssue Tracker:https://github.com/calmjs/calmjs.sassy/issuesSource Code:https://github.com/calmjs/calmjs.sassyLegalThecalmjs.sassypackage is part of the calmjs project.The calmjs project is copyright (c) 2016 Auckland Bioengineering
Institute, University of Auckland.calmjs.sassyis licensed under
the terms of the GPLv2 or later.Changelog1.0.2 (2020-04-23)Useself.openerfor 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 basecalmjs.scssregistry to allow Python packages to
export.scssfiles for their dependants to utilize.Provide a basecalmjs scssruntime to interface with the defaultlibsass-pythontoolchain for the production of.cssartifacts
for any given Python packages.Provide a couple artifact builders that integrates with the calmjs
artifact production framework.