8000 Export our JS using AMD-compatible modules (2) by almarklein · Pull Request #271 · flexxui/flexx · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Export our JS using AMD-compatible modules (2) #271

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 65 commits into from
Nov 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
281b28d
PyScript: remove support for import syntax
almarklein Sep 29, 2016
a365db7
PyScript: better reporting of JSError (invalid PyScript)
almarklein Sep 29, 2016
468dbbc
fix examples that used import
almarklein Sep 29, 2016
9990099
WIP refactoring asset store and session assets
almarklein Sep 29, 2016
8f76de8
Fix png module shape
almarklein Oct 13, 2016
5eda2d4
remove session_id query string from url at JS init time
almarklein Oct 14, 2016
7f931f4
Refactoring of tornadoserver and some renaming
almarklein Oct 14, 2016
65f903a
Add example
almarklein Oct 14, 2016
44a0f52
Use a single Asset class. simpliefies add_asset() and add_shared_asset()
almarklein Oct 16, 2016
7577db7
Improve auto-dependency of pyscript-stdlib.js
almarklein Oct 16, 2016
6248ebf
PyScript: create_js_module move/clean/test
almarklein Oct 17, 2016
6ebab3a
License header + "use strict" handled in the right places
almarklein Oct 17, 2016
6d33bc2
PyScript docs
almarklein Oct 17, 2016
a1d0f06
Improve AMD loader and a bit of cleanup
almarklein Oct 17, 2016
db9be87
Make export work again. Plus several tweaks to remote assets.
almarklein Oct 24, 2016
d3988c6
Make data work for exported apps
almarklein Oct 24, 2016 8000
d1a1086
asset store tests
almarklein Oct 24, 2016
4dd378b
style fixes
almarklein Oct 24, 2016
30c04dd
post-rebase tweak
almarklein Oct 24, 2016
55d07b6
Simplify naming of Model classes in JS
almarklein Oct 24, 2016
08e4d8b
Help users that used modules transition to new system
almarklein Oct 24, 2016
51b315a
Fixes for notebook (though notebook is still broken)
almarklein Oct 24, 2016
13bceea
Cleanup
almarklein Oct 24, 2016
3ac04b7
apparently this object literal notation does not work in older browsers
almarklein Oct 24, 2016
6e8c125
suppress error in the right place
almarklein Oct 24, 2016
072304c
Improvements to (dynamic) asset loading
almarklein Oct 24, 2016
adcb1ad
Interactive mode!
almarklein Oct 24, 2016
f7be0d1
Improve how we have a default active model.
almarklein Oct 25, 2016
9cdde45
Update pyscript docs
almarklein Oct 25, 2016
29fec28
Update app docs (mostly about assets/data)
almarklein Oct 25, 2016
78237e0
fix benchmark example
almarklein Oct 27, 2016
1e844b7
Always use our own Flexx loader.
almarklein Oct 28, 2016
e5e0f5a
Asset sources can also be Python modules
almarklein Nov 1, 2016
f284033
Make notebook work in JLab and also better overall.
almarklein Nov 4, 2016
75973d7
Disable our Phosphor CSS when in JLab
almarklein Nov 4, 2016
c1bdec9
Made notebook flag more clear
almarklein Nov 4, 2016
02ca8d8
Dont let our CSS clash with other Phosphor-related CSS.
almarklein Nov 4, 2016
680dc83
style
almarklein Nov 4, 2016
ccbac96
All Phosphor CSS is provided by phosphor-all
almarklein Nov 7, 2016
ecf7134
Better CSS (more modular)
almarklein Nov 7, 2016
93f5b28
Prefix url where flexx stuff is served with "flexx"
almarklein Nov 8, 2016
6e0ae3a
ws.check_origin more flexx agnostic
almarklein Nov 9, 2016
d47769e
app server object can be created without binding/listening.
almarklein Nov 9, 2016
fb0479a
consistent special app names
almarklein Nov 9, 2016
4d37b1c
Merge pull request #264 from zoofIO/jlab
almarklein Nov 9, 2016
58af306
PyScript provided meta info, e.g. what names are unknown/globals.
almarklein Nov 2, 2016
118101c
JSModule, Bundles, and lighter Assets
almarklein Nov 2, 2016
59e290a
Tests for modules, assets, assetstore
almarklein Nov 16, 2016
b5e0fb8
JSModule consider None as stub, but warn. pyscript has stubs module.
almarklein Nov 16, 2016
c1f63a5
PyScript improvements (e.g. better aware of globals)
almarklein Nov 16, 2016
5085ff5
docs and examples plus a few tweaks
almarklein Nov 16, 2016
586066d
Cleanup and small fixes
almarklein Nov 16, 2016
d093661
More cleanup
almarklein Nov 16, 2016
cdfb10f
Merge pull request #266 from zoofIO/modules3
almarklein Nov 16, 2016
41a4936
Fix tests
almarklein Nov 16, 2016
dbd5222
forgot to add asset tests
almarklein Nov 16, 2016
21320de
Ditch implicit asset association, but use assets.associate_asset()
almarklein Nov 16, 2016
98489ec
fix tests
almarklein Nov 17, 2016
81235fd
fix tests
almarklein Nov 17, 2016
d17c5d2
isinstance now works by virtue of modules!
almarklein Nov 17, 2016
27ce26d
mmm some modules do not have __file__
almarklein Nov 17, 2016
2a89e02
add_shared_asset returns (relative) url
almarklein Nov 17, 2016
a20a1cc
Add example for using Python in JS
almarklein Nov 17, 2016
4765468
Added session.remove_data()
almarklein Nov 17, 2016
9eec27c
rrr flake
almarklein Nov 17, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions docs/app/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ Functions related to the event loop

.. autofunction:: flexx.app.run

.. autofunction:: flexx.app.init_notebook

.. autofunction:: flexx.app.stop

.. autofunction:: flexx.app.init_interactive

.. autofunction:: flexx.app.init_notebook

.. autofunction:: flexx.app.call_later

.. autofunction:: flexx.app.create_server
Expand Down Expand Up @@ -46,17 +48,23 @@ The Model class
Session and Assets
------------------

The session handles the connection between Python and the JavaScript,
An asset is represented using an ``Asset`` object that defines its sources,
dependencies, etc. Assets can be shared or specific to the session.
The AssetStore provides all shared assets for clients connected to the current
process. The global store is at ``flexx.app.assets``.
The session object handles the connection between Python and the JavaScript,
and it allows adding client-side assets, which for instance makes it
easy to create extensions based on existing JS libraries.

The AssetStore provides all assets for clients connected to the current
process. The global store is at ``flexx.app.assets``.

.. autoclass:: flexx.app.Session
:inherited-members:

.. autoclass:: flexx.app.Asset
:members:


.. autoclass:: flexx.app.assetstore.AssetStore
:members:

.. autoclass:: flexx.app.Session
:inherited-members:
:members:
15 changes: 5 additions & 10 deletions docs/pyscript/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ PyScript API

.. autofunction:: flexx.pyscript.get_full_std_lib

.. autofunction:: flexx.pyscript.get_all_std_names

.. autofunction:: flexx.pyscript.create_js_module

----

Most users probably want to use the above functions, but you can also
Expand All @@ -24,18 +28,9 @@ get closer to the metal by using and/or extending the parser class.

The PyScript module has a few dummy constants that can be imported and
used in your code to let e.g. pyflakes know that the variable exists. E.g.
``from flexx.pyscript.stubs import undefined, window``.
``from flexx.pyscript import undefined, window``.

.. data:: undefined
.. data:: window
.. data:: root
.. data:: document
.. data:: console
.. data:: module
.. data:: typeof
.. data:: require
.. data:: Object
.. data:: Math
.. data:: RegExp
.. data:: Infinity
.. data:: NaN
13 changes: 8 additions & 5 deletions docs/scripts/uiexample.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,25 @@ def create_ui_example(filename, to_root, height=300):
filename_abs = os.path.join(HTML_DIR, *filename_parts)
filename_rel = to_root + '/' + '/'.join(filename_parts)

# Import
# Import - mod_name must be unique, because JS modules match Py modules
try:
mod_name = "app_" + fname[:-3]
if sys.version_info >= (3, 5):
spec = importlib.util.spec_from_file_location("example", filename)
spec = importlib.util.spec_from_file_location(mod_name, filename)
m = importlib.util.module_from_spec(spec)
sys.modules[mod_name] = m # Flexx needs to be able to access the module
spec.loader.exec_module(m)
else: # http://stackoverflow.com/a/67692/2271927
from importlib.machinery import SourceFileLoader
m = SourceFileLoader("example", filename).load_module()
m = SourceFileLoader(mod_name, filename).load_module()
sys.modules[mod_name] = m
except Exception as err:
10000 err_text = str(err)
msg = 'Example not generated. <pre>%s</pre>' % err_text
if os.environ.get('READTHEDOCS', False):
msg = 'This example is not build on read-the-docs. <pre>%s</pre>' % err_text
open(filename_abs, 'wt', encoding='utf-8').write(msg)
warnings.warn('Could not import ui example: %s' % err_text)
warnings.warn('Could not import ui example in %s: %s' % (filename, err_text))
return get_html(filename_rel, 60)

# Get class name
Expand All @@ -88,7 +91,7 @@ def create_ui_example(filename, to_root, height=300):

# Export
try:
app.export(m.__dict__[class_name], filename_abs, single=False)
app.export(m.__dict__[class_name], filename_abs, link=2, write_shared=False)
except Exception as err:
err_text = str(err)
msg = 'Example not generated. <pre>%s</pre>' % err_text
Expand Down
6 changes: 3 additions & 3 deletions flexx/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def cmd_info(self, port=None):
return self.cmd_help('info')
port = int(port)
try:
print(http_fetch('http://localhost:%i/__cmd__/info' % port))
print(http_fetch('http://localhost:%i/flexx/cmd/info' % port))
except FetchError:
print('There appears to be no local server at port %i' % port)

Expand All @@ -91,7 +91,7 @@ def cmd_stop(self, port=None):
return self.cmd_help('stop')
port = int(port)
try:
print(http_fetch('http://localhost:%i/__cmd__/stop' % port))
print(http_fetch('http://localhost:%i/flexx/cmd/stop' % port))
print('stopped server at %i' % port)
except FetchError:
print('There appears to be no local server at port %i' % port)
Expand All @@ -103,7 +103,7 @@ def cmd_log(self, port=None, level='info'):
if port is None:
return self.cmd_help('log')
print('not yet implemented')
#print(http_fetch('http://localhost:%i/__cmd__/log' % int(port)))
#print(http_fetch('http://localhost:%i/flexx/cmd/log' % int(port)))


class FetchError(Exception):
Expand Down
207 changes: 169 additions & 38 deletions flexx/app/__init__.py
BEA9 < F438 /tr>
Original file line number Diff line number Diff line change
@@ -1,64 +1,204 @@
"""
The app module implements the connection between Python and JavaScript.
It runs a web server and websocket server based on Tornado, provides
an asset (and data) management system, and provides the ``Model`` class,
which allows definition of objects that have both a Python and JavaScript
representation, forming the basis for widgets etc.

Writing code with the Model class
---------------------------------

An application will typically consists of a custom Model class (or Widget).
In the class, one can define Python behavior, JavaScript behavior, define
properties etc. Using the event system explained in ``flexx.event``, one
can listen for events on either side (Python or JavaScript). Check out
the examples and this simplistic code:

.. code-block:: py

from flexx import app, event

class MyModel(app.Model):

def init(self):
...

# Listen for changes of foo in Python
@event.connect('foo')
def on_foo(self, *events):
...

class Both:

# A property that exists on both ends (and is synced)
@event.property
def foo(self, v=0):
return v

class JS:

# Listen for changes of foo in JS
@event.connect('foo')
def on_foo(self, *events):
...

The scope of modules
--------------------

The above demonstrates how one can write code that is executed in JavaScript.
In this code, you can make use of functions, classes, and values that are
defined in the same module (as long as they can be transpiled / serialized).

For every Python module that defines code that is used in JS, a corresponding
JS module is created. Flexx detects what variable names are used in the JS
code, but not declared in it, and tries to find the corresponding object in
the module. You can even import functions/classes from other modules.

.. code-block:: py

from flexx import app

from foo import func1

def func2():
...

info = {'x': 1, 'y': 2}

class MyModel(app.Model):

class JS:

@event.connect('some.event')
def handler(self, *events):
func1(info)
func2()

In the code above, Flexx will include the definition of ``func2`` and
``info`` in the same module that defines ``MyModel``, and include
``func1`` in the JS module ``foo``. If the JS in ``MyModel`` would not
use these functions, neither definition would be included in the JavaScript.

One can also assign ``__pyscript__ = True`` to a module to make Flexx transpile
a module as a whole.

It implements a simple server based on Tornado. HTML is served to
provide the client with the JavaScript and CSS, but once connected, all
co 4D1F mmunication goed via a websocket.

A central component is the ``Model`` class, which allows definition of
objects that have both a Python and JavaScript representation, forming
a basis for model-view-like systems.

Some background info on the server process
------------------------------------------

Each server process hosts on a single URL (domain+port), but can serve
multiple applications (via different paths). Each process uses one
tornado IOLoop, and exactly one Tornado Application object.

Applications
------------

A ``Model`` class can be made into an application by decorating it with
A ``Model`` class can be made into an application by passing it to
``app.serve``. This registers the application, so that clients can connect
to the app based on its name. One instance of this class is instantiated
to the app based on its name (or using a custom name specified in
``app.serve()``). One instance of this class is created
per connection. Multiple apps can be hosted from the same process simply
be specifying more app classes. To connect to the application
corresponding to the `MyApp` class, one should connect to
"http://domain:port/MyApp".

An app can also be launched (via ``app.launch()``), which will invoke
a client webruntime which is connected to the returned app object. This
is the intended way to launch desktop-like apps. An app can also be
exported to HTML via ``app.export()``.
is the intended way to launch desktop-like apps.

An app can also be exported to HTML via ``app.export()``. One can
create a drectory structure that contains multiple exported apps that
share assets, or export apps as standalone html documents.

Starting the server
-------------------

Use ``start()`` to enter the mainloop for the server. For desktop
applications you can use ``run()``, which does what ``start()`` does,
Use ``app.start()`` to enter the mainloop for the server. For desktop
applications you can use ``app.run()``, which does what ``start()`` does,
except the main loop exits when there are no more connections (i.e. the
server stops when the window is closed).
server stops when the (last) window is closed).

In the notebook
Interactive use
---------------

Further, Flexx can be used interactively, from an IDE or from the Jupyter
notebook. Use ``app.init_interactive()`` to launch a runtime in the same
way as ``app.launch()``, except one can now interactively (re)define models
and widgets, and make them appear in the runtime/browser.

In the IPython/Jupyter notebook, the user needs to run
``init_notebook()`` which will inject JS and CSS into the browser.
``init_notebook()`` which will inject the necessary JS and CSS.
Simple widgets (e.g. buttons) will display just fine, but for other
widgets you might want to use ``SomeWidget(style='height:300px')`` to
specify its size.


Asset management
----------------

When writing code that relies on a certain JS or CSS library, that library
can be loaded in the client by associating it with the module that needs it.
Flexx will then automatically load it when code from that module is used in JS:

.. code-block:: py

# Associate asset
app.assets.associate_asset(__name__, 'mydep.js', js_code)

# Sometimes a more lightweight *remote* asset is prefered
app.assets.associate_asset(__name__, 'http://some.cdn/lib.css')

# Create Model (or Widget) that needs the asset at the client
class MyMode(app.Model):
....

It is also possible to provide assets that are not automatically loaded
on the main app page, e.g. for sub-pages or web workers:

.. code-block:: py

# Register asset
asset_url = app.assets.add_shared_asset('mydep.js', js_code)

Data management
---------------

Data can be provided per session or shared between sessions:

.. code-block:: py

# Add session-specific data
link = your_model.session.add_data('some_name.png', binary_blob)

# Add shared data
link = app.assets.add_shared_data('some_name.png', binary_blob)


Note that ``binary_blob`` can also be a string starting with ``http://`` or
``file://``. In the future we plan to make it easier to load arbitrary
data in the client (mainly for scientific purposes).

Note that this API for providing the client with data may change in a
following release. If you rely on this, please make an issue so we can
work out a smooth transition if necessary.

Some background info on the server process
------------------------------------------

Each server process hosts on a single URL (domain+port), but can serve
multiple applications (via different paths). Each process uses one
tornado IOLoop, and exactly one Tornado Application object.

When a client connects to the server, it is served an HTML page, which
contains the information needed to connect to a websocket. From there,
all communication happens over this websocket.

"""

_DEV_NOTES = """
Overview of classes:

* Model: the base class for creating Python-JS objects.
* JSModule: represents a module in JS that corresponds to a Python module.
* Asset: represents an asset.
* Bundle: an Asset subclass to represent a collecton of JSModule's in one asset.
* AssetStore: one instance of this class is used to provide all client
assets in this process (JS, CSS, images, etc.).
* SessionAssets: base class for Session that implements the assets part.
Assets specific to the session are name-mangled.
assets in this process (JS, CSS, images, etc.). It also keeps track
of modules.
* SessionAssets: base class for Session that implements the assets/data part.
* Session: object that handles connection between Python and JS. Has a
websocket, and optionally a reference to the runtime.
* WebSocket: tornado WS handler.
Expand All @@ -67,7 +207,7 @@
* Server: handles http requests. Uses manager to create new app
instances or get the page for a pending session. Hosts assets by using
the global asset store.
* FlexxJS (in clientcore.py): more or less the JS side of a session.
* Flexx class (in clientcore.py): more or less the JS side of a session.

"""

Expand All @@ -80,14 +220,5 @@
from .model import Model, get_active_model
from .model import get_instance_by_id, get_model_classes
from .funcs import create_server, current_server, run, start, stop, call_later
from .funcs import init_notebook, serve, launch, export
from .assetstore import assets
from .clientcore import FlexxJS

from ..pyscript.stdlib import get_full_std_lib as _get_full_std_lib


_JS_TEMPLATE = "%s\nvar flexx = new FlexxJS();"

assets.add_asset('pyscript-std.js', _get_full_std_lib().encode())
assets.create_module_assets('flexx.app', js=_JS_TEMPLATE % FlexxJS)
from .funcs import init_interactive, init_notebook, serve, launch, export
from .assetstore import assets, Asset, Bundle, JSModule
Loading
0