├── .eslintrc
├── .gitignore
├── ChangeLog
├── README.rst
├── dev-requirements.txt
├── fileupload
├── __init__.py
├── static
│ ├── extension.js
│ └── widget.js
└── widget.py
├── requirements.txt
├── setup.cfg
└── setup.py
/.eslintrc:
--------------------------------------------------------------------------------
1 | globals:
2 | requirejs: true
3 | env:
4 | browser: true
5 | amd: true
6 | rules:
7 | no-unserscore-dangle:
8 | - 2
9 | quotes:
10 | - 2
11 | - single
12 | brace-style:
13 | - 2
14 | - stroustrup
15 | - allowSingleLine: true
16 | func-style:
17 | - 2
18 | - declaration
19 | consistent-this:
20 | - 0
21 | - self
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # PyInstaller
28 | # Usually these files are written by a python script from a template
29 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
30 | *.manifest
31 | *.spec
32 |
33 | # Installer logs
34 | pip-log.txt
35 | pip-delete-this-directory.txt
36 |
37 | # Unit test / coverage reports
38 | htmlcov/
39 | .tox/
40 | .coverage
41 | .coverage.*
42 | .cache
43 | nosetests.xml
44 | coverage.xml
45 | *,cover
46 |
47 | # Translations
48 | *.mo
49 | *.pot
50 |
51 | # Django stuff:
52 | *.log
53 |
54 | # Sphinx documentation
55 | docs/_build/
56 |
57 | # PyBuilder
58 | target/
59 |
--------------------------------------------------------------------------------
/ChangeLog:
--------------------------------------------------------------------------------
1 | CHANGES
2 | =======
3 |
4 | * clarify README.rst, fix setup.cfg
5 |
6 | 0.1.4
7 | -----
8 |
9 | * Make button label a traitlet
10 | * fix typo
11 |
12 | 0.1.3
13 | -----
14 |
15 | * add missing \`install\` instruction
16 | * cleanup
17 | * update installation instructions
18 | * add Jupyter extensions hooks
19 | * Update requirements.txt
20 | * Update README.rst
21 | * update README
22 |
23 | 0.1.1
24 | -----
25 |
26 | * fixes #4
27 |
28 | 0.1
29 | ---
30 |
31 | * Add shields.io badges
32 | * update setup.cfg, add dev requirements
33 | * Set the universal flag
34 | * closes #3
35 | * Use PY2 compatible super call
36 | * migrate to \`notebook\` 4.0
37 | * \`data\` is now a bytes string
38 | * Update installation instruction
39 | * Install into user directory if system fails
40 | * use a label element
41 | * Use a fake button w/ a nested file input
42 | * add IPython dependency requirement
43 | * update README
44 | * add decode method
45 | * add view and model
46 | * initial commit, scaffold
47 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | IPython File Upload
2 | ===================
3 |
4 | .. image:: https://img.shields.io/pypi/v/fileupload.svg
5 | :target: https://pypi.python.org/pypi/fileupload/
6 | :alt: Latest Version
7 |
8 | .. image:: https://img.shields.io/pypi/dm/fileupload.svg
9 | :target: https://pypi.python.org/pypi/fileupload/
10 | :alt: Downloads
11 |
12 | .. image:: https://beta.mybinder.org/badge.svg
13 | :target: https://beta.mybinder.org/v2/gh/draperjames/fileupload2binder/master?filepath=index.ipynb
14 | :alt: Binder
15 |
16 | An IPython notebook widget to upload files, using FileReader_.
17 |
18 | Installation
19 | ------------
20 |
21 | Install using pip::
22 |
23 | pip install fileupload
24 |
25 | Install JavaScript::
26 |
27 | jupyter nbextension install [--user] --py fileupload
28 |
29 | Enable the extension::
30 |
31 | jupyter nbextension enable [--user] --py fileupload
32 |
33 | Usage
34 | -----
35 |
36 | .. code-block:: python
37 |
38 | import io
39 | from IPython.display import display
40 | import fileupload
41 |
42 | def _upload():
43 |
44 | _upload_widget = fileupload.FileUploadWidget()
45 |
46 | def _cb(change):
47 | decoded = io.StringIO(change['owner'].data.decode('utf-8'))
48 | filename = change['owner'].filename
49 | print('Uploaded `{}` ({:.2f} kB)'.format(
50 | filename, len(decoded.read()) / 2 **10))
51 |
52 | _upload_widget.observe(_cb, names='data')
53 | display(_upload_widget)
54 |
55 | _upload()
56 |
57 |
58 | Base64 data is synced to the ``data_base64`` member, decoded data can be
59 | obtained from ``data``.
60 | The name of the uploaded file is stored in ``filename``.
61 |
62 | Changelog
63 | ---------
64 |
65 | Refer to Changelog_.
66 |
67 | .. _FileReader: https://developer.mozilla.org/en-US/docs/Web/API/FileReader
68 | .. _Changelog: ./ChangeLog
69 |
--------------------------------------------------------------------------------
/dev-requirements.txt:
--------------------------------------------------------------------------------
1 | twine>=1.6.5
2 |
--------------------------------------------------------------------------------
/fileupload/__init__.py:
--------------------------------------------------------------------------------
1 | from .widget import FileUploadWidget # noqa
2 |
3 |
4 | def _jupyter_nbextension_paths():
5 | return [dict(
6 | section='notebook',
7 | src='static',
8 | dest='fileupload',
9 | require='fileupload/extension')]
10 |
--------------------------------------------------------------------------------
/fileupload/static/extension.js:
--------------------------------------------------------------------------------
1 | if (window.require) {
2 | window.require.config({
3 | map: {
4 | '*': {
5 | 'fileupload': 'nbextensions/fileupload/widget',
6 | 'jupyter-js-widgets': 'nbextensions/jupyter-js-widgets/extension'
7 | }
8 | }
9 | });
10 | }
11 |
--------------------------------------------------------------------------------
/fileupload/static/widget.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'jupyter-js-widgets'
4 | ], function ($, widgets) {
5 |
6 | 'use strict';
7 | var _getId = (function () {
8 |
9 | var cnt = 0;
10 | return function () {
11 |
12 | cnt += 1;
13 | return 'fileupload_' + cnt;
14 | }
15 | })();
16 |
17 | var FileUploadView = widgets.DOMWidgetView.extend({
18 |
19 | render: function render () {
20 |
21 | FileUploadView.__super__.render.apply(this, arguments);
22 | var id = _getId();
23 | var label = this.model.get('label');
24 | this.model.on('change:label', this._handleLabelChange, this);
25 | var $label = $('')
26 | .text(label)
27 | .addClass('btn btn-default')
28 | .attr('for', id)
29 | .appendTo(this.$el);
30 |
31 | $('')
32 | .attr('type', 'file')
33 | .attr('id', id)
34 | .css('display', 'none')
35 | .appendTo($label);
36 | },
37 |
38 | _handleLabelChange: function() {
39 | var label = this.model.get('label');
40 | this.$el.children("label").contents().first().replaceWith(label);
41 | },
42 |
43 | events: {
44 | 'change': '_handleFileChange'
45 | },
46 | _handleFileChange: function _handleFileChange (ev) {
47 |
48 | var file = ev.target.files[0];
49 | var that = this;
50 | if (file) {
51 | var fileReader = new FileReader();
52 | fileReader.onload = function fileReaderOnload () {
53 |
54 | that.model.set('data_base64', fileReader.result);
55 | that.touch();
56 | };
57 | fileReader.readAsDataURL(file);
58 | }
59 | else {
60 | that.send({ event: 'Unable to open file.' });
61 | }
62 | that.model.set('filename', file.name);
63 | that.touch();
64 | }
65 | });
66 |
67 | return { FileUploadView: FileUploadView };
68 | });
69 |
--------------------------------------------------------------------------------
/fileupload/widget.py:
--------------------------------------------------------------------------------
1 | import base64
2 | import ipywidgets
3 | import traitlets
4 |
5 |
6 | class FileUploadWidget(ipywidgets.DOMWidget):
7 | '''File Upload Widget.
8 | This widget provides file upload using `FileReader`.
9 | '''
10 | _view_name = traitlets.Unicode('FileUploadView').tag(sync=True)
11 | _view_module = traitlets.Unicode('fileupload').tag(sync=True)
12 |
13 | label = traitlets.Unicode(help='Label on button.').tag(sync=True)
14 | filename = traitlets.Unicode(help='Filename of `data`.').tag(sync=True)
15 | data_base64 = traitlets.Unicode(help='File content, base64 encoded.'
16 | ).tag(sync=True)
17 | data = traitlets.Bytes(help='File content.')
18 |
19 | def __init__(self, label="Browse", *args, **kwargs):
20 | super(FileUploadWidget, self).__init__(*args, **kwargs)
21 | self._dom_classes += ('widget_item', 'btn-group')
22 | self.label = label
23 |
24 | def _data_base64_changed(self, *args):
25 | self.data = base64.b64decode(self.data_base64.split(',', 1)[1])
26 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | ipywidgets>=5.1
2 | traitlets>=4.2
3 | notebook>=4.2
4 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | name = fileupload
3 | author = Alain Péteut
4 | author-email = alain.peteut@yahoo.com
5 | home-page = https://github.com/peteut/ipython-file-upload
6 | summary = IPython file upload widget
7 | description-file = README.rst
8 | license = MIT
9 | classifier =
10 | Development Status :: 4 - Beta
11 | Intended Audience :: Science/Research
12 | Framework :: IPython
13 | License :: OSI Approved :: MIT License
14 | Programming Language :: Python :: 2.7
15 | Programming Language :: Python :: 3
16 | Programming Language :: Python :: 3.3
17 | Programming Language :: Python :: 3.4
18 | License :: OSI Approved :: MIT License
19 | keywords =
20 | file
21 | upload
22 | ipython
23 | filereader
24 | [files]
25 | packages = fileupload
26 | package_data =
27 | static = *.js
28 | [bdist_wheel]
29 | universal = 1
30 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | setup(
4 | setup_requires=['pbr'],
5 | pbr=True,
6 | )
7 |
--------------------------------------------------------------------------------