Skip to content

Commit b515321

Browse files
committed
Updated README
1 parent 182c67f commit b515321

15 files changed

+236
-191
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
.DS_Store
2-
.ipynb_checkpoints
2+
.ipynb_checkpoints
3+
build
4+
p2j.egg-info
5+
dist

MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include README.md LICENSE p2j/templates/*.json

README.md

+27-15
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
1-
# py2nb (Beta)
1+
# p2j - Python to Jupyter Notebook
22

33
Converts Python source code to Jupyter notebook.
44

5-
The purpose of this repo is so that we can run a code paragraph-by-paragraph and don't have to do that by copying each paragraph of the code into every cell. It's also useful if we want to run our code in Google Colab.
5+
The purpose of this package is so that we can run a code paragraph-by-paragraph and don't have to do that by copying each paragraph of the code into every cell. It's also useful if we want to run our code in Google Colab.
6+
7+
In a nutshell, every paragraph of your code is transformed into a code cell.
68

79
This parser isn't perfect, but you would be satisfactorily pleased with what you get.
810

911
## Installing
1012

1113
```bash
12-
git clone https://github.com/raibosome/code2notebook.git
14+
pip install p2j
1315
```
1416

1517
## Running
1618

1719
```bash
18-
python py2nb.py code_to_parse.py
20+
p2j code_to_parse.py
1921
```
2022

21-
and you will get a `code_to_parse.ipynb` Jupyter notebook. See `python py2nb.py -h` for other arguments.
23+
and you will get a `code_to_parse.ipynb` Jupyter notebook. See `p2j -h` for other arguments.
2224

2325
The `examples/example.py` is a Keras tutorial on building an autoencoder for the MNIST dataset, found [here](https://github.com/keras-team/keras/blob/master/examples/mnist_denoising_autoencoder.py). You can run the example:
2426

2527
```bash
26-
python py2nb.py examples/example.py
28+
p2j examples/example.py
2729
```
2830

2931
## Tests
@@ -41,20 +43,30 @@ Jupyter notebooks are just JSON files. The `py2nb.py` reads the source code line
4143
## Project Structure
4244

4345
```txt
44-
├── example.py Example code
45-
├── py2nb.py The code that parses and generates the notebook
46-
└── templates JSON files that make up the final Jupyter notebook
47-
├── cell_code.json
48-
├── cell_markdown.json
49-
└── metadata.json
46+
├── p2j The parser module
47+
│ ├── __init__.py
48+
│ ├── examples Example codes that you can parse
49+
│ ├── p2j.py Main file
50+
│ └── templates JSON files needed to build the notebook
51+
├── README.md This file
52+
├── LICENSE Licensing
53+
├── MANIFEST.in Python packaging-related
54+
├── build Python packaging-related
55+
├── dist Python packaging-related
56+
├── p2j.egg-info Python packaging-related
57+
└── setup.py Python packaging-related
5058
```
5159

5260
## Code format
5361

54-
The parser assumes a format where your code is paragraphed. Each paragraph has the comments part and/or the code part. The comments will be automatically converted to a markdown cell while the code will be, you guessed it, the code cell.
62+
There is no specific format that you should follow, but generally the parser assumes a format where your code is paragraphed. Each paragraph has the comments part and/or the code part. The comments will be automatically converted to a markdown cell while the code will be, you guessed it, the code cell.
5563

56-
Some examples of well-documented code:
64+
Some examples of well-documented code (and from which you can test!):
5765

5866
- [PyTorch Tutorials](https://pytorch.org/tutorials/beginner/pytorch_with_examples.html)
5967
- [Keras Examples](https://github.com/keras-team/keras/tree/master/examples)
60-
- [Scikit Learn Example](https://scikit-learn.org/stable/auto_examples/classification/plot_digits_classification.html#sphx-glr-auto-examples-classification-plot-digits-classification-py)
68+
- [Scikit Learn Example](https://scikit-learn.org/stable/auto_examples/classification/plot_digits_classification.html#sphx-glr-auto-examples-classification-plot-digits-classification-py)
69+
70+
## Pull requests
71+
72+
Pull requests are very much encouraged!

p2j/__init__.py

Whitespace-only changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

p2j/p2j.py

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
"""
2+
This code translate .py files to .ipynb
3+
"""
4+
# Standard imports for file handling and JSON files
5+
import argparse
6+
import os
7+
import json
8+
import sys
9+
10+
# Reserved Python keywords
11+
RESERVED = ['for', 'with', 'class', 'while']
12+
HERE = os.path.abspath(os.path.dirname(__file__))
13+
14+
def main():
15+
16+
# Get source and target filenames
17+
parser = argparse.ArgumentParser(description='Parse a file.')
18+
parser.add_argument('source_filename', help='File to parse')
19+
parser.add_argument('-t', '--target_filename', help='Target filename')
20+
parser.add_argument('-o', '--overwrite', action='store_true',
21+
help='Flag to overwrite existing target file')
22+
args = parser.parse_args()
23+
source_filename = args.source_filename
24+
target_filename = args.target_filename
25+
overwrite = args.overwrite
26+
27+
# Check if source file exists and read
28+
try:
29+
with open(source_filename, 'r') as file:
30+
data = [l.rstrip('\n') for l in file]
31+
except FileNotFoundError:
32+
print("Source file not found. Specify a valid source file.")
33+
sys.exit(1)
34+
35+
# Check if target file is specified and exists. If not specified, create
36+
if target_filename is None:
37+
target_filename = os.path.splitext(source_filename)[0] + ".ipynb"
38+
print("Target file not specified. Creating a default notebook with name {}.".format(
39+
target_filename))
40+
if not overwrite and os.path.isfile(target_filename):
41+
print("File {} exists. Add -o flag to overwrite or specify a different name.".format(target_filename))
42+
sys.exit(1)
43+
44+
# Read JSON files for .ipynb template
45+
with open(HERE + '/templates/cell_code.json') as file:
46+
code = json.load(file)
47+
with open(HERE + '/templates/cell_markdown.json') as file:
48+
markdown = json.load(file)
49+
with open(HERE + '/templates/metadata.json') as file:
50+
misc = json.load(file)
51+
52+
# Initialise variables
53+
final = {}
54+
cells = []
55+
arr = []
56+
num_lines = len(data)
57+
58+
# Initialise variables for checks
59+
is_block_comment = False
60+
end_paragraph = True
61+
is_running_comment = False
62+
is_running_code = False
63+
next_is_code = False
64+
next_is_nothing = False
65+
next_is_comment = False
66+
is_running_function = False
67+
next_is_function = False
68+
69+
# Read source code line by line
70+
for i, line in enumerate(data):
71+
72+
buffer = ""
73+
74+
# Check next line
75+
try:
76+
next_is_code = data[i+1][0] != "#"
77+
except:
78+
pass
79+
try:
80+
next_is_comment = data[i+1][0] == "#"
81+
except:
82+
pass
83+
try:
84+
next_is_nothing = data[i+1] == ""
85+
except:
86+
pass
87+
try:
88+
next_is_function = data[i+1][:4] == " " or (
89+
data[i+1] == "" and data[i+2][:4] == " ")
90+
# print(line)
91+
# print(data[i+1][:4] == "")
92+
except:
93+
pass
94+
end_of_code = i == num_lines-1
95+
96+
# Skip if line is empty
97+
if line == "":
98+
continue
99+
100+
# Sub-paragraph is a comment but not a running code
101+
if (is_running_comment or (line[0] == "#" and (line[:8] != "# pylint" or line[:7] != "#pylint")) or line[:3] == "'''" or line[-3:] == "'''" or line[:3] == "\"\"\"" or line[-3:] == "\"\"\"") and not is_running_code:
102+
103+
if line[:3] == "'''" or line[-3:] == "'''" or line[:3] == "\"\"\"" or line[-3:] == "\"\"\"":
104+
is_block_comment = not is_block_comment
105+
106+
if is_block_comment:
107+
buffer = line.replace("'''", "").replace("\"\"\"", "")
108+
else:
109+
buffer = line[2:]
110+
111+
# Wrap this sub-paragraph as a cell
112+
# if next line is code or next line is space or end of code
113+
if end_of_code or (next_is_code and not is_block_comment) or (next_is_nothing and not is_block_comment):
114+
arr.append(f"{buffer}")
115+
markdown["source"] = arr
116+
cells.append(dict(markdown))
117+
arr = []
118+
is_running_comment = False
119+
else:
120+
buffer = buffer + "<br>"
121+
arr.append(f"{buffer}")
122+
is_running_comment = True
123+
continue
124+
else: # Sub-paragraph is a comment but not a running code
125+
buffer = line
126+
127+
# Close this if next line is end of code or next is nothing
128+
# Don't close if next is still part of a
129+
# or not next_is_function) or (not next_is_function and next_is_nothing):
130+
if (end_of_code or next_is_nothing) and not (next_is_nothing and next_is_function):
131+
arr.append(f"{buffer}")
132+
code["source"] = arr
133+
cells.append(dict(code))
134+
arr = []
135+
is_running_code = False
136+
else:
137+
buffer = buffer + "\n"
138+
139+
# Put another newline character if in a function
140+
try:
141+
if data[i+1] == "" and (data[i+2][:5] == " #" or data[i+2][:9] == " #"):
142+
buffer = buffer + "\n"
143+
except:
144+
pass
145+
146+
arr.append(f"{buffer}")
147+
is_running_code = True
148+
continue
149+
150+
# Finalise the contents of notebook
151+
final["cells"] = cells
152+
final.update(misc)
153+
154+
# Write JSON to target file
155+
with open(target_filename, 'w') as outfile:
156+
json.dump(final, outfile)
157+
print("Notebook {} written.".format(target_filename))
158+
159+
160+
if __name__ == "__main__":
161+
print("Convert a Python script to Jupyter notebook")
162+
main()
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)