summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--docs/differences/index_template.txt8
-rw-r--r--tools/gen-cpydiff.py213
2 files changed, 221 insertions, 0 deletions
diff --git a/docs/differences/index_template.txt b/docs/differences/index_template.txt
new file mode 100644
index 0000000000..6ade2c2dab
--- /dev/null
+++ b/docs/differences/index_template.txt
@@ -0,0 +1,8 @@
+MicroPython Differences from CPython
+====================================
+
+The operations listed in this section produce conflicting results in MicroPython when compared to standard Python.
+
+.. toctree::
+ :maxdepth: 2
+
diff --git a/tools/gen-cpydiff.py b/tools/gen-cpydiff.py
new file mode 100644
index 0000000000..6f83bb6e86
--- /dev/null
+++ b/tools/gen-cpydiff.py
@@ -0,0 +1,213 @@
+# This file is part of the MicroPython project, http://micropython.org/
+#
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Rami Ali
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+""" gen-cpydiff generates documentation which outlines operations that differ between MicroPython
+ and CPython. This script is called by the docs Makefile for html and Latex and may be run
+ manually using the command make gen-cpydiff. """
+
+import os
+import errno
+import subprocess
+import time
+import re
+from collections import namedtuple
+
+TESTPATH = '../tests/cpydiff/'
+UPYPATH = '../unix/micropython'
+DOCPATH = '../docs/genrst/'
+INDEXTEMPLATE = '../docs/differences/index_template.txt'
+INDEX = 'index.rst'
+
+HEADER = '.. This document was generated by tools/gen-cpydiff.py\n\n'
+UIMPORTLIST = {'struct', 'collections', 'json'}
+CLASSMAP = {'Core': 'Core Language', 'Types': 'Builtin Types'}
+INDEXPRIORITY = ['syntax', 'core_language', 'builtin_types', 'modules']
+RSTCHARS = ['=', '-', '~', '`', ':']
+SPLIT = '"""\n|categories: |description: |cause: |workaround: '
+TAB = ' '
+
+Output = namedtuple('output', ['name', 'class_', 'desc', 'cause', 'workaround', 'code',
+ 'output_cpy', 'output_upy', 'status'])
+
+def readfiles():
+ """ Reads test files """
+ tests = list(filter(lambda x: x.endswith('.py'), os.listdir(TESTPATH)))
+ tests.sort()
+ files = []
+
+ for test in tests:
+ text = open(TESTPATH + test, 'r').read()
+
+ try:
+ class_, desc, cause, workaround, code = [x.rstrip() for x in \
+ list(filter(None, re.split(SPLIT, text)))]
+ output = Output(test, class_, desc, cause, workaround, code, '', '', '')
+ files.append(output)
+ except IndexError:
+ print('Incorrect format in file ' + TESTPATH + test)
+
+ return files
+
+def uimports(code):
+ """ converts CPython module names into MicroPython equivalents """
+ for uimport in UIMPORTLIST:
+ uimport = bytes(uimport, 'utf8')
+ code = code.replace(uimport, b'u' + uimport)
+ return code
+
+def run_tests(tests):
+ """ executes all tests """
+ results = []
+ for test in tests:
+ with open(TESTPATH + test.name, 'rb') as f:
+ input_cpy = f.read()
+ input_upy = uimports(input_cpy)
+
+ process = subprocess.Popen('python', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+ output_cpy = [com.decode('utf8') for com in process.communicate(input_cpy)]
+
+ process = subprocess.Popen(UPYPATH, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+ output_upy = [com.decode('utf8') for com in process.communicate(input_upy)]
+
+ if output_cpy[0] == output_upy[0] and output_cpy[1] == output_upy[1]:
+ status = 'Supported'
+ print('Supported operation!\nFile: ' + TESTPATH + test.name)
+ else:
+ status = 'Unsupported'
+
+ output = Output(test.name, test.class_, test.desc, test.cause,
+ test.workaround, test.code, output_cpy, output_upy, status)
+ results.append(output)
+
+ results.sort(key=lambda x: x.class_)
+ return results
+
+def indent(block, spaces):
+ """ indents paragraphs of text for rst formatting """
+ new_block = ''
+ for line in block.split('\n'):
+ new_block += spaces + line + '\n'
+ return new_block
+
+def gen_table(contents):
+ """ creates a table given any set of columns """
+ xlengths = []
+ ylengths = []
+ for column in contents:
+ col_len = 0
+ for entry in column:
+ lines = entry.split('\n')
+ for line in lines:
+ col_len = max(len(line) + 2, col_len)
+ xlengths.append(col_len)
+ for i in range(len(contents[0])):
+ ymax = 0
+ for j in range(len(contents)):
+ ymax = max(ymax, len(contents[j][i].split('\n')))
+ ylengths.append(ymax)
+
+ table_divider = '+' + ''.join(['-' * i + '+' for i in xlengths]) + '\n'
+ table = table_divider
+ for i in range(len(ylengths)):
+ row = [column[i] for column in contents]
+ row = [entry + '\n' * (ylengths[i]-len(entry.split('\n'))) for entry in row]
+ row = [entry.split('\n') for entry in row]
+ for j in range(ylengths[i]):
+ k = 0
+ for entry in row:
+ width = xlengths[k]
+ table += ''.join(['| {:{}}'.format(entry[j], width - 1)])
+ k += 1
+ table += '|\n'
+ table += table_divider
+ return table + '\n'
+
+def gen_rst(results):
+ """ creates restructured text documents to display tests """
+
+ # make sure the destination directory exists
+ try:
+ os.mkdir(DOCPATH)
+ except OSError as e:
+ if e.args[0] != errno.EEXIST and e.args[0] != errno.EISDIR:
+ raise
+
+ toctree = []
+ class_ = []
+ for output in results:
+ section = output.class_.split(',')
+ for i in range(len(section)):
+ section[i] = section[i].rstrip()
+ if section[i] in CLASSMAP:
+ section[i] = CLASSMAP[section[i]]
+ if i >= len(class_) or section[i] != class_[i]:
+ if i == 0:
+ filename = section[i].replace(' ', '_').lower()
+ rst = open(DOCPATH + filename + '.rst', 'w')
+ rst.write(HEADER)
+ rst.write(section[i] + '\n')
+ rst.write(RSTCHARS[0] * len(section[i]))
+ rst.write(time.strftime("\nGenerated %a %d %b %Y %X UTC\n\n", time.gmtime()))
+ toctree.append(filename)
+ else:
+ rst.write(section[i] + '\n')
+ rst.write(RSTCHARS[min(i, len(RSTCHARS)-1)] * len(section[i]))
+ rst.write('\n\n')
+ class_ = section
+ rst.write('**' + output.desc + '**\n\n')
+ if output.cause != 'Unknown':
+ rst.write('**Cause:** ' + output.cause + '\n\n')
+ if output.workaround != 'Unknown':
+ rst.write('**Workaround:** ' + output.workaround + '\n\n')
+
+ rst.write('Sample code::\n\n' + indent(output.code, TAB) + '\n')
+ output_cpy = indent(''.join(output.output_cpy[0:2]), TAB).rstrip()
+ output_cpy = ('::\n\n' if output_cpy != '' else '') + output_cpy
+ output_upy = indent(''.join(output.output_upy[0:2]), TAB).rstrip()
+ output_upy = ('::\n\n' if output_upy != '' else '') + output_upy
+ table = gen_table([['CPy output:', output_cpy], ['uPy output:', output_upy]])
+ rst.write(table)
+
+ template = open(INDEXTEMPLATE, 'r')
+ index = open(DOCPATH + INDEX, 'w')
+ index.write(HEADER)
+ index.write(template.read())
+ for section in INDEXPRIORITY:
+ if section in toctree:
+ index.write(indent(section + '.rst', TAB))
+ toctree.remove(section)
+ for section in toctree:
+ index.write(indent(section + '.rst', TAB))
+
+def main():
+ """ Main function """
+
+ # clear search path to make sure tests use only builtin modules
+ os.environ['MICROPYPATH'] = ''
+
+ files = readfiles()
+ results = run_tests(files)
+ gen_rst(results)
+
+main()