File: README.rst

package info (click to toggle)
python-calmjs 3.4.2-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,312 kB
  • sloc: python: 15,771; javascript: 56; makefile: 20
file content (1002 lines) | stat: -rw-r--r-- 41,618 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
calmjs
======

A Python framework for building toolchains and utilities for working
with the Node.js ecosystem from within a Python environment.

.. image:: https://github.com/calmjs/calmjs/actions/workflows/build.yml/badge.svg?branch=3.4.2
    :target: https://github.com/calmjs/calmjs/actions/workflows/build.yml?query=branch:3.4.2
.. image:: https://ci.appveyor.com/api/projects/status/45054tm9cfk7ryam/branch/3.4.2?svg=true
    :target: https://ci.appveyor.com/project/metatoaster/calmjs/branch/3.4.2
.. image:: https://coveralls.io/repos/github/calmjs/calmjs/badge.svg?branch=3.4.2
    :target: https://coveralls.io/github/calmjs/calmjs?branch=3.4.2

.. |AMD| replace:: AMD (Asynchronous Module Definition)
.. |calmjs.bower| replace:: ``calmjs.bower``
.. |calmjs| replace:: ``calmjs``
.. |calmjs_npm| replace:: ``calmjs npm``
.. |calmjs.rjs| replace:: ``calmjs.rjs``
.. |calmjs.webpack| replace:: ``calmjs.webpack``
.. |npm| replace:: ``npm``
.. |r.js| replace:: ``r.js``
.. |setuptools| replace:: ``setuptools``
.. |webpack| replace:: ``webpack``
.. |yarn| replace:: ``yarn``
.. _AMD: https://github.com/amdjs/amdjs-api/blob/master/AMD.md
.. _Bower: https://bower.io/
.. _calmjs.bower: https://pypi.python.org/pypi/calmjs.bower
.. _calmjs.rjs: https://pypi.python.org/pypi/calmjs.rjs
.. _calmjs.webpack: https://pypi.python.org/pypi/calmjs.webpack
.. _Node.js: https://nodejs.org/
.. _npm: https://www.npmjs.com/
.. _r.js: https://github.com/requirejs/r.js
.. _setuptools: https://pypi.python.org/pypi/setuptools
.. _webpack: https://webpack.js.org/
.. _yarn: https://yarnpkg.com/


Introduction
------------

Calmjs defines an extensible framework for interoperability between
Python and `Node.js`_ runtime for Python packages, to provide their
developers a well defined interface for bi-directional access between
Node.js/Javascript development tools and the JavaScript code within
their Python packages, such that a proper, formal integration with
Node.js/JavaScript environment from a Python environment can be
facilitated.  The goal of the Calmjs framework is to aid the
development, testing, and deployment of Python packages that also
include and/or integrate with external JavaScript code.


Methodology
-----------

First, this is achieved by providing Python packages the ability to
declare dependencies on Node.js/JavaScript packages or source files that
are required to complete their functionality.  This common framework
will ensure the accessibility of these metadata under a common protocol,
to avoid incompatible declarations that are not portable between
different projects and environments, or being otherwise scattered across
different tools or locations or be duplicated within the same working
environments by different sets of tools.  Without a common framework,
the result is that Python packages will be unable to properly
communicate their non-Python requirements and states between each other,
resulting in difficulties in building, development and deployment of the
software stack, and/or becoming a source of errors for those processes.

Second, by offering a set of tools built on top of this extensible
framework to work with these declarations for generating the
configuration files for required Node.js tools, so that they can
construct the required the build and/or runtime environment for their
functionality.

Ultimately, this permits better Node.js integration with a given Python
environment, lowering the amount of effort needed to achieve continuous
integration and/or delivery of Python packages in conjunction with
Node.js/JavaScript packages in a reproducible manner.

Implementation
~~~~~~~~~~~~~~

In order to achieve this, the Calmjs framework provides a set of
extension to |setuptools|_ that assists with the tracking and management
of dependencies of JavaScript or Node.js based packages (such as ones
through |npm|_) for a given Python package.  It also provides a number
of base classes that can be used to build custom toolchains that
implement different strategies for managing and compiling required
JavaScript code and related assets into the deployment artifacts that an
application server may use, or to generate test harnesses to ensure
correctness under both the development and production environment.
These extra functionalities will be provided by other Python packages
under the |calmjs| namespace in order to realize this modular
architecture.

The name Calmjs was originally derived from the steps in the first
iteration of the toolchain which involves the steps compile, assemble,
and linkage into a module of JavaScript using the namespace from the
host Python package.  The `m` in the logo is the ear of a rabbit.  The
reason this animal is chosen as the mascot for this project is because
of their dietary habits, as it's analogous to how JavaScript code is
typically turned into a minimally usable level by other tools and
framework.


Features overview
-----------------

A framework for integration with Node.js based package managers
    Through |setuptools| command hooks, |calmjs| provides Python
    packages with the ability to declare and manage manifest definition
    files for Node.js based package management systems (e.g. such as
    ``package.json`` for |npm|).  Under typical usage, this means the
    declaration of ``dependencies`` or ``devDependencies`` for the
    JavaScript packages needed by a given Python package can be tracked,
    all within the |setuptools| framework through the extensions
    provided by |calmjs|.

    The other part of this infrastructure is that these declarations
    follow the Python package dependency graph.  Developers and users
    can make use of the |calmjs| console command entry point, or through
    |setuptools|, to generate a manifest file to facilitate the
    installation of Node.js packages required by the Python packages
    within the completed application stack, tailored for all the
    packages at hand.

    |calmjs| includes the integration support for both |npm|_ and
    |yarn|_ by default.

Export JavaScript code out of Python packages with the same namespace
    A given Python package that included associated JavaScript source
    code within the same Python module and namespace structure alongside
    Python source code within the source tree, will be able to declare
    those namespaces as the root for those JavaScript modules under the
    exact same Python package namespace through |setuptools| entry
    points.

    These declarations will be available through registries exposed by
    the |calmjs| module registry system for other packages to turn those
    declarations through the API provided by the framework into working
    JavaScript code following the same declared module and namespace
    structures.  The default module registry will make use of the ``/``
    character (instead of the ``.`` character like in Python) as the
    separator for the names due to established naming conventions in
    JavaScript (and in ES6 towards the future).

    Other tools that works with the Calmjs framework can then make use
    of these raw JavaScript source files in conjunction with the local
    Node.js environment, or generate artifacts for deployment over the
    web.  This leads to...

Better integration of JavaScript toolchains into Python environments
    This is achieved by providing a framework for building toolchains
    for working with tools written in JavaScript for Node.js/JavaScript
    environments that integrates properly with existing Python packages
    and environments.

    There are no limitations as to how or what can be done with the
    tools or the source files, as this is left as an implementation
    detail.  For an example please refer to the |calmjs.rjs|_ Python
    package, which allows the production of |AMD|_ artifacts from
    JavaScript packages embedded inside Python packages, or
    |calmjs.webpack|_ which integrates with |webpack|_ for the
    production of another commonly used bundled artifact format.

    Generally, toolchains can be built to find and load all Python
    packages (through the |calmjs| registry system) that have any
    JavaScript source files, and those will be extracted, go through the
    appropriate transpilers (if any) in order to build deployable
    artifacts.  Test harnesses can be set up to aid with running of unit
    tests, functional testing and naturally the final integration tests
    needed for a successful deployment.

Well-defined modular architecture to ensure code reuse and extensibility
    The features described so far are built upon a foundation of generic
    classes and modules, so that the support for additional JavaScript
    tools or custom process for handling transpilation can be as simple
    as creating a new module for a couple of classes with additional
    parameters with the relevant |setuptools| entry points.

    In fact, |calmjs| out of the box only ships with just the core
    framework plus the |npm|/|yarn| interfacing part, with the support
    for tools like `Bower`_ or |r.js|_ as completely separate packages
    (as |calmjs.bower|_ and |calmjs.rjs|_ respectively), such that
    projects, environments or sites that do not need the functionality
    those packages provide can simply opt to not have them installed.


Installation
------------

As the goal of |calmjs| is to integrate Node.js and |npm| (or |yarn|)
into a Python environment, they need to be available within the
environment; if they are not installed please follow the installation
steps for `Node.js`_ appropriate for the target operating
system/environment/platform.

To install |calmjs| into a given Python environment, the following
command can be executed to install directly from PyPI:

.. code:: console

    $ pip install calmjs

.. _development installation method:

Alternative installation methods (for developers, advanced users)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Development is still ongoing with |calmjs|, for the latest features and
bug fixes, the development version can be installed through git like so:

.. code:: console

    $ # standard installation mode
    $ pip install git+https://github.com/calmjs/calmjs.git#egg=calmjs
    $ # for an editable installation mode; note the upgrade flag
    $ pip install -U -e git+https://github.com/calmjs/calmjs.git#egg=calmjs

Note that the ``-U`` flag for the editable installation is to ensure
that |setuptools| be upgraded to the latest version to avoid issues
dealing with namespaces for development packages, which is documented in
the next paragraph.

Alternatively, the git repository can be cloned directly and execute
``python setup.py develop`` while inside the root of the source
directory, however if this development installation method is done using
any version of |setuptools| earlier than v31, there will be inconsistent
errors with importing of modules under the |calmjs| namespace.  Various
`symptoms of namespace import failures`_ are documented under the
`troubleshooting`_ section of this document.

Testing the installation
~~~~~~~~~~~~~~~~~~~~~~~~

To ensure that the |calmjs| installation is functioning correctly, the
built-in testsuite can be executed by the following:

.. code:: console

    $ python -m unittest calmjs.tests.make_suite

If there are failures, please file an issue on the issue tracker with
the full traceback, and/or the method of installation.  Please also
remember to include platform specific information, such as Python
version, operating system environments and version, and other related
information related to the issue at hand.


Usage and description of key features
-------------------------------------

When installed to a particular Python environment, the |calmjs|
command-line utility will become available within there.

.. code:: console

    $ calmjs
    usage: calmjs [-h] [-d] [-q] [-v] [-V] <command> ...

    positional arguments:
      <command>
        artifact     helpers for the management of artifacts
        npm          npm support for the calmjs framework
        yarn         yarn support for the calmjs framework

    optional arguments:
      -h, --help     show this help message and exit

    global options:
      -d, --debug    show traceback on error; twice for post_mortem
                     '--debugger' when execution cannot continue
      -q, --quiet    be more quiet
      -v, --verbose  be more verbose
      -V, --version  print version information

As mentioned, |npm| support is built-in so it is always available; to
access its help, simply execute ``calmjs npm -h``, which will then list
the options available for that particular subcommand.  If other
subcommands are available (which will be provided by other |calmjs|
integration packages) they will be listed as a ``<command>`` and their
specific help messages will be accessible in the same manner.

Declare a ``package.json`` for a given Python package
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. _using package_json:

If a package wish to declare dependencies on packages hosted by |npm|,
it may do something like this in its ``setup.py``:

.. code:: python

    from setuptools import setup

    package_json = {
        "dependencies": {
            "jquery": "~3.0.0",
            "underscore": "~1.8.0",
        }
    }

    setup(
        name='example.package',
        # ...
        setup_requires=[
            'calmjs',
            # plus other setup_requires ...
        ],
        package_json=package_json,
        # ...
    )

Note that ``setup_requires`` section must specify |calmjs| in order to
enable the ``package_json`` setup keyword for the generation of the
``package.json`` metadata file for the given package whenever ``python
setup.py egg_info`` is executed (directly or indirectly), so that even
if |calmjs| is not already installed into the current Python
environment, it will be acquired from PyPI and be included as part of
the |setuptools| setup process, and without being a direct dependency of
the given package.  The ``package.json`` will be generated if the
provided data is either a valid JSON string or a dictionary without
incompatible data types.  For example:

.. code:: console

    $ python setup.py egg_info
    running egg_info
    writing package_json to example.package.egg-info/package.json
    ...
    $ cat example.package.egg-info/package.json
    {
        "dependencies": {
            "jquery": "~3.0.0",
            "underscore": "~1.8.0"
        }
    }

The key reason for using ``setup_requires`` is to not force a given
package's dependents to have |calmjs| as part of their dependencies, as
typically this is a requirement only for developers but not for
end-users.  This also mean that for developers that want to use |calmjs|
and utilities they must install that separately (i.e. ``pip install
calmjs``), or declare |calmjs| as a development dependency through the
usage of ``extras_require`` flag, for example:

.. code:: python

    setup(
        name='example.package',
        # ...
        setup_requires=[
            'calmjs',
            # ...
        ],
        extras_require={
            'dev': [
                'calmjs',
                # ... plus other development dependencies
            ],
        },
        # ...
    )

Then to fully install the package as an editable package with the
dependencies listed under the ``dev`` extras:

.. code:: console

    $ pip install -e .[dev]
    Obtaining file://example/package
    ...
    Installing collected packages: ..., calmjs, ...
      Running setup.py develop for example.package
    Successfully installed ...

Note that now the |calmjs| package remains installed in the Python
environment, and the utilities they provide may now be used, covered by
the following sections.

Using the ``package.json`` across Python package dependencies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. |integrate_with_calmjs_npm| replace:: integration with ``npm``
    through ``calmjs npm``

With the ``package.json`` file written to the package metadata
directory, utilities such as |calmjs| make make use of it.  One method
to do this is through that package's ``setup.py``.  By invoking
``setup.py npm --init`` from there, a new ``package.json``, combined
with all the ``dependencies`` and ``devDependencies`` declared by the
Python package dependencies of the given package, will be written to the
current directory.  This is akin to running ``npm init``, with the
difference being that the dependencies are also being resolved through
the Python package dependency tree for the given Python package.

Do note that this requires the ``package.json`` creation and handling
capability be available for the given package (refer to previous section
on how to achieve this) and all dependencies must be correctly installed
and be importable from the current Python environment.

Alternatively, the |calmjs_npm| utility may be used.  Invoking ``calmjs
npm --init example.package`` from the command line will achieve the same
thing, anywhere on the file system, provided that both |calmjs| and
``example.package`` are installed and available through the current
Python environment's import system.  For more details and information
about this utility, please refer to the |integrate_with_calmjs_npm|_
section.

Dealing with |npm| dependencies with Python package dependencies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Flat is better than nested.  So all ``dependencies`` (and
``devDependencies``) declared by any upstream Python package will be
automatically inherited by all its downstream packages, but they have
the option to override it with whatever they want through the mechanism
as described above.  They can set a JavaScript or Node.js package to
whatever versions desired, or even simply remove that dependency
completely by setting the version to ``None``.

Whenever an actual ``package.json`` is needed by |calmjs|, the
|calmjs_npm| utility flattens all Node.js dependencies needed by the
Python packages into a single file, which is then passed into the
respective JavaScript package manager for consumption.  This process is
also done when a |calmjs| toolchain or utility make use of these
declared information to to generate the desired artifacts to achieve
whatever desired task at hand.

Of course, if the nested style of packages and dependency in the same
style as |npm| is desired, no one is forced to use this, they are free
to use whatever tools to interpret or make use of whatever data files
and dependencies available, and/or to split their packages up to Python
and JavaScript bits and have them be deployed and hosted on both PyPI
(for ``pip``) and |npm| respectively and then figure out how to bring
them back together in a coherent manner.  Don't ask (or debate with) the
author on how the latter option is better or easier for everyone
(developers, system integrators and end-users) involved.

Declare explicit dependencies on paths inside ``node_modules``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Given that the dependencies on specific versions of packages sourced
from |npm| is explicitly specified, build tools will benefit again from
explicit declarations on files needed from those packages.  Namely, the
compiled packages could be declared in the ``extras_calmjs`` section in
JSON string much like ``package_json``, like so:

.. code:: python

    extras_calmjs = {
        'node_modules': {
            'jquery': 'jquery/dist/jquery.js',
            'underscore': 'underscore/underscore.js',
        },
    }

    setup(
        name='example.package',
        ...
        extras_calmjs=extras_calmjs,
        ...
    )

Since ``node_modules`` is declared to be an ``extras_key``, conflicting
declarations between packages within the environment will be resolved
and merged in the same manner as dependencies conflicts declared in
``package_json``.

Please do note that complete path names must be declared (note that the
``.js`` filename suffix is included in the example); directories can
also be declared.  However, as these declarations are done from within
Python, explicit, full paths are required thus it is up to downstream
integration packages to properly handle and/or convert this into the
conventions that standard Node.js tools might expect (i.e. where the
``.js`` filename suffix is omitted).

Export JavaScript code from Python packages
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Furthering the previous example, if the files and directories inside
``example.package`` are laid out like so::

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

To declare the JavaScript source files within ``./example/package`` as
JavaScript modules through |calmjs|, an entry point can be declared like
so in the ``setup.py`` file:

.. code:: python

    setup(
        ...
        entry_points="""
        ...
        [calmjs.module]
        example.package = example.package
        """,
        ...
    )

Note that the name of the entry point is not relevant; that entry point
name is ignored, as the intention of the default module registry is to
provide a module name that maps directly to the same import namespace
as the source Python module, but with the ES5 namespace separator
``/``, instead of the ``.`` character as in Python.  If an explicit
mapping is required, a new module registry class may be defined that
uses the provided name as the CommonJS import name from the JavaScript
code.

The default method will expose the two source files with the following
names::

    - 'example/package/ui'
    - 'example/package/widget'

For some projects, it may be undesirable to permit this automated method
to extract all the available JavaScript source files from within the
given Python module.

To get around this, it is possible to declare new module registries
through the Calmjs framework.  Provided that the ``ModuleRegistry``
subclass was set up correctly to generate the desired modules from a
given package, simply declare this as a ``calmjs.registry`` entry point
like so:

.. code:: python

    setup(
        ...
        entry_points="""
        ...
        [calmjs.registry]
        example.module = example.package.registry:ExampleModuleRegistry
        """,
        ...
    )

Do note that while the names permitted for an entry point name is quite
unrestricted, these registry names should be of a standard dotted
namespace format to ensure maximum tool compatibility, as these can be
specified from the command line through tools that utilizes this system.

Once the registry was declared, simply replace ``calmjs.module`` with
the name of that, along with a ``calmjs_module_registry`` attribute that
declare this ``example.module`` registry is the default registry to use
with this package.

.. code:: python

    setup(
        ...
        calmjs_module_registry=['example.package'],
        entry_points="""
        ...
        [example.module]
        example.package = example.package
        """,
        ...
    )

Within the Calmjs framework, tools can be explicitly specified to
capture modules from any or all module registries registered to the
framework.  One other registry was also defined.  If the entry point was
declared like so:

.. code:: python

    setup(
        ...
        entry_points="""
        ...
        [calmjs.py.module]
        example.package = example.package
        """,
        ...
    )

The separator for the namespace and the module will use the ``.``
character instead of ``/``.  However given that the ``.`` character is a
valid name for a JavaScript module, the usage of this may create issues
with certain JavaScript tools.  While AMD based module systems can
generally handle ``.`` characters in imports without issues, allowing
somewhat more Python-like feel importing using dotted names within the
JavaScript environment, however, this may lead to incompatibilities with
other JavaScript libraries thus the usage of this naming scheme is not
recommended.

By default, another registry with the ``.tests`` suffix is also declared
as a compliment to the previously introduced registries, which packages
can make use of to declare JavaScript test code that accompanies the
respective modules that have been declared.  For example:

.. code:: python

    setup(
        ...
        entry_points="""
        ...
        [calmjs.module]
        example.package = example.package

        [calmjs.module.tests]
        example.package.tests = example.package.tests
        """,
        ...
    )

Much like the first example, this declares ``example.package`` as a
Python namespace module that exports JavaScript code, with the
subsequent declaration section being the module that contains the tests
that accompanies the first.

Make available the accompanied resource files to loaders
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Certain Node.js build tools and frameworks support the concept of
"loaders" for other resource files through the same import/require
system for JavaScript modules.  To provide the most basic support to
ease the effort required by package creators to expose other resource
files to the Node.js tooling import systems, a subsidiary loader
registry may be declared and used in conjunction with a parent module
registry.  To extend on the previous example, if following entry points
are defined:

.. code:: ini

    [calmjs.module]
    example.package = example.package

    [calmjs.module.loader]
    json = json[json]
    text = text[txt,json]

The ``calmjs.module.loader`` registry will reference its parent registry
``calmjs.module`` for the specific modules that have been exposed, so
that it will source all the relevant filenames with the declared file
name extensions to a specific loader by name.  Unlike the base module
registry, the module loader registry will ignore the Python module name
section, while the name (on the left-hand side) is the desired loader,
with the extras (comma-separated tokens enclosed between the ``[]``) are
the file name extensions to be acquired from the package.  Thus with the
previously defined entries for the ``example.package``, the following
``require`` statements should resolve if the target resource files exist
for the package:

.. code:: javascript

    // resolved through "json = ...[json]"
    var manifest_obj = require('json!example/package/manifest.json');
    // resolved through "text = ...[txt,json]"
    var manifest_txt = require('text!example/package/manifest.json');
    var index_txt = require('text!example/package/index.txt');

As the values generated by the registry follow the standard toolchain
spec compile entry grammar, this should satisfy the most basic use cases
and can be included directly as a ``calmjs_module_registry`` for the
package.  However, the actual usage in the provided JavaScript code
conjunction with the actual toolchain packages that integrates/interacts
with with their respective Node.js packages may have interactions that
will require special handling (such as inclusion/exclusion of the
generated segments targeted for the final artifact, or how those sources
are aliased or made available to the system, or whether or not the
registry itself requires manual integration); please consult the
documentation for the specific integration package with regards to this
specific registry type.

One final note: due to the limitations of the Python entry point system,
file name extensions are assumed to be all lower-case.

.. _integrate_with_calmjs_npm:

Integration with |npm| through |calmjs_npm|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

As mentioned, it is possible to make use of the ``package.json``
generation capabilities from outside of |setuptools|.  Users can easily
do the same through the built-in ``calmjs npm`` tool:

.. code:: console

    $ calmjs npm --help
    usage: calmjs npm [-h] [-d] [-q] [-v] [-V] [--view] [--init] [--install]
                      [-i] [-m] [-w] [-E] [-P] [-D]
                      <package> [<package> ...]

    npm support for the calmjs framework

    positional arguments:
      <package>          python packages to be used for the generation of
                         'package.json'

    optional arguments:
      -D, --development  explicitly specify development mode for npm install
      -E, --explicit     explicit mode disables resolution for dependencies;
                         only the specified Python package(s) will be used.
      -h, --help         show this help message and exit
      -i, --interactive  enable interactive prompt; if an action requires an
                         explicit response but none were specified through
                         flags (i.e. overwrite), prompt for response;
                         disabled by default
      -m, --merge        merge generated 'package.json' with the one in
                         current directory; if interactive mode is not
                         enabled, implies overwrite, else the difference will
                         be displayed
      -P, --production   explicitly specify production mode for npm install
      -w, --overwrite    automatically overwrite any file changes to current
                         directory without prompting

Naturally, the same ``--init`` functionality shown above with the
|setuptools| framework is available, however package names can be
supplied for generating the target ``package.json`` file from anywhere
on the filesystem, provided that the Python environment has all the
required packages installed.  For instance, if the Node.js packages for
``example.package`` is to be installed, this can be invoked to view the
``package.json`` that would be generated:

.. code:: console

    $ calmjs -v npm --view example.package
    2016-09-01 16:37:18,398 INFO calmjs.cli generating a flattened
    'package.json' for 'example.package'
    {
        "dependencies": {
            "jquery": "~3.0.0",
            "underscore": "~1.8.0",
        },
        "devDependencies": {},
        "name": "example.package"
    }

If there is an existing ``package.json`` file already in the current
directory, using the ``-i`` flag with ``--init`` or ``--install`` will
show what differences there may be between the generated version and the
existing version, and prompt for an action.

Toolchain
~~~~~~~~~

Documentation on how to extend the Toolchain class to support use cases
is currently incomplete.  This is usually combined together with a
``calmjs.runtime.DriverRuntime`` to hook into the ``calmjs`` runtime.

Unfortunately at this time a detailed guide on how to create a complete
implementation is not completed (only documentation within the class
are, however).  For a working example on how this may be achieved please
refer to the implementations provided by |calmjs.rjs|_ or
|calmjs.webpack|_.

Toolchain Advice
~~~~~~~~~~~~~~~~

For package developers that need to provide additional instructions to
a toolchain execution (e.g. for compatibility between RequireJS and also
webpack for specific use case of features to a given package), the
toolchain system will also make use of the advice system such that
additional instructions may be created and registered for use and reuse
by their dependents.  Much like the Toolchain, this feature is currently
lacking in documentation outside of the test cases.

Pre-defined artifact generation through |setuptools|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

It is possible to define the artifacts to be generated for a given
package and the rule to do so.  Simply define a function that return an
instance of a ``calmjs.toolchain.Toolchain`` subclass that have
integrated with the desired tool, and a ``calmjs.toolchain.Spec`` object
with the rules needed.  These specific functions are often provided by
the package that offers them, please refer to the toolchain packages
listed and linked in the previous section for further details on how
these might be used.

As these are also implemented through the registry system, the entry
points generally look like this:

.. code:: python

    setup(
        ...
        build_calmjs_artifacts=True,
        entry_points="""
        ...
        [calmjs.artifacts]
        complete.bundle.js = example.toolchain:builder
        """,
        ...
    )

In the example, the ``builder`` function from the module
``example.toolchain`` is used to generate the ``complete.bundle.js``
file.  The generated artifact files will reside in the
``calmjs_artifacts`` directory within the package metadata directory
(one that ends with either ``.dist-info`` or ``.egg-info``) for that
package.  An accompanied ``calmjs_artifacts.json`` file will also be
generated, listing the versions of the various Python packages that were
involved with construction of that artifact, and the version of binary
that was used for the task.

When the ``build_calmjs_artifacts`` is set to ``True``, the hook for
automatic generation of these artifacts through the ``setup.py build``
step will enabled.  This is useful for automatically bundling the
artifact file with a release such as Python wheels (e.g. running
``setup.py bdist_wheel`` will also build the declared artifacts.
Otherwise, this step can be manually invoked using
``setup.py build_calmjs_artifacts`` or through the
``calmjs artifact build`` tool.


Troubleshooting
---------------

The following may be some issues that may be encountered with typical
usage of |calmjs|.

CRITICAL calmjs.runtime terminating due to a critical error
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If |calmjs| encounters any unexpected situation, it may abort like so:

.. code:: console

    $ calmjs npm --install calmjs.dev
    CRITICAL calmjs.runtime terminating due to a critical error

If no useful ERROR message is listed before, please try running again
using a debug flag (either ``-d`` or ``--debug``).

.. code:: console

    $ calmjs -d npm --install calmjs.dev
    CRITICAL calmjs.runtime terminating due to exception
    Traceback (most recent call last):
    ...

Specifying the debug flag twice will enable the ``post_mortem`` mode,
where a debugger will be fired at the point of failure.  Authors of
packages that implement runtime classes that provide subcommands to the
|calmjs| command may find this useful during their development cycles.
Do note that the default debugger is only triggered if the top level
runtime class enable the usage (which the runtime class that implement
the |calmjs| command does) and if the failure occur during the
invocation of the runtime class.  Any other errors or exceptions that
occur during the setup stage of the |calmjs| runtime will simply be
logged at a lower priority level (e.g. to make warnings generated during
the setup stage visible, additional verbose flags must be provided).

ERROR bad 'calmjs.runtime' entry point
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ImportError
    This is typically caused by improper removal of locally installed
    packages that had an entry point registered, an addon package to
    |calmjs| registered entry points pointing to bad import locations,
    or conflicting installation methods was used for the current
    environment as outlined in the installation section of this
    document.  Either reinstall the broken package again with the
    correct installation method for the environment, or fully uninstall
    or remove files belonging to the packages or sources that are
    triggering the undesirable error messages.

bad entry point
    This is caused by packages defining malformed entry point.  The name
    of the package triggering this error will be noted in the log; the
    error may be reported to its developer.

.. _symptoms of namespace import failures:

Random ``ImportError`` when trying to import from the |calmjs| namespace
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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 a `development installation method`_ (e.g. using ``python setup.py
develop``) into the Python environment in conjunction with another
package that was installed through ``pip`` within the same namespace.
Failures can manifest as inconsistent import failures for any modules
under the |calmjs| namespace.  As an example:

.. __: https://setuptools.readthedocs.io/en/latest/history.html#v31-0-0

.. code:: pycon

    >>> from calmjs import tests
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ImportError: cannot import name tests
    >>> from calmjs import parse  # calmjs.parse was installed via pip
    >>> from calmjs import tests
    >>> # no failure, but it was failing just earlier?

It could also manifest differently, such as an ``AttributeError``, which
may be triggered through the execution of unittests for |calmjs|:

.. code:: console

    $ coverage run --include=src/* -m unittest calmjs.tests.make_suite
    Traceback (most recent call last):
      ...
        parent, obj = obj, getattr(obj, part)
    AttributeError: 'module' object has no attribute 'tests'
    $ python -m calmjs.tests.make_suite
    /usr/bin/python: No module named 'calmjs.tests'

To resolve this issue, ensure that |setuptools| is upgraded to v31 or
greater, which may be installed/upgraded through ``pip`` like so:

.. code:: console

    $ pip install --upgrade setuptools

Then reinstall all the required packages that are under the |calmjs|
namespace to resolve this import issue.

Environmental variables being ignored/not passed to underlying tools
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Generally speaking, the Calmjs framework filters out all environmental
variables except for the bare minimum by default, and only passes a
limited number to the underlying tool.  These are the ``PATH`` and the
``NODE_PATH`` variables, plus platform specific variables to enable
execution of scripts and binaries.

Runtime reporting 'unrecognized arguments:' on declared arguments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This issue should be fully resolved for calmjs>=3.1.0.

The default behavior in the ArgumentParser defaults to uselessly blaming
the root parser for any unrecognized arguments caused by its subparsers.
The original workaround prior to calmjs-3.1.0 had the failure as
documented below as its subparser resolver implementation was
incomplete.  Either of these misleading behaviors impede the end users
from being able to quickly locate the misplaced argument flags.

For instance, if the |calmjs| command was executed like so resulting in
error message may look like this:

.. code:: console

    $ calmjs subcmd1 subcmd2 --flag item
    usage: calmjs subcmd1 ... [--flag FLAG]
    calmjs subcmd1: error: unrecognized arguments: --flag

This means that ``--flag`` is unrecognized by the second subcommand
(i.e. the ``calmjs subcmd1 subcmd2`` command) as that was placed after
``subcmd2``, but the subparser for ``subcmd1`` flagged that as an error.
Unfortunately there are a number of issues in the ``argparse`` module
that makes it difficult to fully resolve this problem, so for the mean
time please ensure the flag is provided at the correct subcommand level
(i.e.  in this case, ``calmjs subcmd1 --flag item subcmd2``), otherwise
consult the help at the correct level by appending ``-h`` to each of the
valid subcommands.

Module registry not locating files from namespace packages
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

There are a number of edge cases associated with namespace packages in
Python, especially if they are provided on the system through different
methods (i.e. mix of zipped eggs, wheels and development packages).
While workarounds for handling of namespace modules for the given
packages are provided, there are limitations in place.  One such cause
is due to complexity in dealing with zipped eggs; if this is an issue,
please ensure that the affected package has ``zip_safe`` declared as
false, or alternatively generate a Python wheel then install that wheel,
if the target Python environment has that as the standard installation
format.

UserWarning: Unknown distribution option: 'package_json'
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This also applies to other relevant options, as it is caused by the
execution of ``setup.py`` without |calmjs| being available to
|setuptools|, such that the handling method for these keywords remain
undefined.  This can be corrected by providing |calmjs| as part of the
``setup_requires`` section.  Further information on this may be found in
the `using package_json`_ section of this document.


Contribute
----------

- Issue Tracker: https://github.com/calmjs/calmjs/issues
- Source Code: https://github.com/calmjs/calmjs


Legal
-----

The Calmjs project is copyright (c) 2016 Auckland Bioengineering
Institute, University of Auckland.  |calmjs| is licensed under the terms
of the GPLv2 or later.