diff --git a/py2nb.py b/py2nb.py index 7d6f6b0..fdf15e1 100755 --- a/py2nb.py +++ b/py2nb.py @@ -22,7 +22,7 @@ # Comment syntax patterns CELL_SPLIT_CHARS = ['#-', '# -'] MARKDOWN_CHARS = ['#|', '# |'] -COMMAND_CHARS = ['#!', '# !'] +COMMAND_CHARS = ['#!', '# !', '#%', '# %'] ACCEPTED_CHARS = CELL_SPLIT_CHARS + MARKDOWN_CHARS + COMMAND_CHARS def new_cell(nb, cell_content, cell_type='code'): @@ -90,8 +90,11 @@ def get_comment_type(line): def extract_content(line, comment_type): """Extract content from comment line based on type.""" if comment_type == 'command': - # Find first ! and return ! plus everything after it - return '!' + line[line.index('!') + 1:].lstrip() + # Find first ! or % and return the command marker plus everything after it + if '!' in line: + return '!' + line[line.index('!') + 1:].lstrip() + elif '%' in line: + return '%' + line[line.index('%') + 1:].lstrip() elif comment_type == 'markdown': # Find first | and return everything after it return line[line.index('|') + 1:] diff --git a/test_py2nb.py b/test_py2nb.py index 21e5194..00d40e8 100644 --- a/test_py2nb.py +++ b/test_py2nb.py @@ -99,6 +99,69 @@ def test_command_blocks(self): self.assertIn('command', command_cell.get('metadata', {}).get('tags', [])) self.assertIn('!pip install numpy', ''.join(command_cell['source'])) + def test_percent_command_blocks(self): + """Test command block creation with #% syntax.""" + script_content = """#| # Test Percent Magic Commands + +#% matplotlib inline +#% load_ext autoreload +#% autoreload 2 + +import numpy as np +import matplotlib.pyplot as plt""" + + script_path = self.create_test_script(script_content) + notebook_path = py2nb.convert(script_path) + + with open(notebook_path, 'r') as f: + nb = json.load(f) + + # Should have: markdown, command, code cells + self.assertEqual(len(nb['cells']), 3) + self.assertEqual(nb['cells'][0]['cell_type'], 'markdown') + self.assertEqual(nb['cells'][1]['cell_type'], 'code') + self.assertEqual(nb['cells'][2]['cell_type'], 'code') + + # Check command cell has command tag + command_cell = nb['cells'][1] + self.assertIn('command', command_cell.get('metadata', {}).get('tags', [])) + self.assertIn('%matplotlib inline', ''.join(command_cell['source'])) + self.assertIn('%load_ext autoreload', ''.join(command_cell['source'])) + + def test_mixed_command_types(self): + """Test mixing #! and #% command types.""" + script_content = """#| # Mixed Command Types Test + +#! pip install numpy +#% matplotlib inline +#% load_ext autoreload +#! pip install matplotlib +#% autoreload 2 + +import numpy as np""" + + script_path = self.create_test_script(script_content) + notebook_path = py2nb.convert(script_path) + + with open(notebook_path, 'r') as f: + nb = json.load(f) + + # Should have: markdown, command, code cells + self.assertEqual(len(nb['cells']), 3) + self.assertEqual(nb['cells'][0]['cell_type'], 'markdown') + self.assertEqual(nb['cells'][1]['cell_type'], 'code') + self.assertEqual(nb['cells'][2]['cell_type'], 'code') + + # Check command cell contains both ! and % commands + command_cell = nb['cells'][1] + self.assertIn('command', command_cell.get('metadata', {}).get('tags', [])) + command_source = ''.join(command_cell['source']) + self.assertIn('!pip install numpy', command_source) + self.assertIn('%matplotlib inline', command_source) + self.assertIn('%load_ext autoreload', command_source) + self.assertIn('!pip install matplotlib', command_source) + self.assertIn('%autoreload 2', command_source) + def test_cell_splits(self): """Test code cell splitting with #- syntax.""" script_content = """x = 1 @@ -125,6 +188,7 @@ def test_mixed_syntax(self): #| This tests all comment types together #! pip install numpy +#% matplotlib inline import numpy as np @@ -135,6 +199,7 @@ def test_mixed_syntax(self): #- #! pip install matplotlib +#% load_ext autoreload import matplotlib.pyplot as plt plt.plot(x)""" @@ -206,6 +271,8 @@ def test_comment_type_detection(self): self.assertEqual(py2nb.get_comment_type('# | markdown'), 'markdown') self.assertEqual(py2nb.get_comment_type('#! install'), 'command') self.assertEqual(py2nb.get_comment_type('# ! install'), 'command') + self.assertEqual(py2nb.get_comment_type('#% magic'), 'command') + self.assertEqual(py2nb.get_comment_type('# % magic'), 'command') self.assertEqual(py2nb.get_comment_type('#- split'), 'split') self.assertEqual(py2nb.get_comment_type('# - split'), 'split') self.assertIsNone(py2nb.get_comment_type('# regular comment')) @@ -214,7 +281,9 @@ def test_content_extraction(self): """Test content extraction from comment lines.""" self.assertEqual(py2nb.extract_content('#| markdown text', 'markdown'), ' markdown text') self.assertEqual(py2nb.extract_content('#! pip install numpy', 'command'), '!pip install numpy') + self.assertEqual(py2nb.extract_content('#% matplotlib inline', 'command'), '%matplotlib inline') self.assertEqual(py2nb.extract_content('# | spaced markdown', 'markdown'), ' spaced markdown') + self.assertEqual(py2nb.extract_content('# % config InlineBackend', 'command'), '%config InlineBackend') def test_file_not_found(self): """Test handling of non-existent files."""