texlive[52174] Master/texmf-dist: pythontex (24sep19)

commits+karl at tug.org commits+karl at tug.org
Tue Sep 24 22:07:38 CEST 2019


Revision: 52174
          http://tug.org/svn/texlive?view=revision&revision=52174
Author:   karl
Date:     2019-09-24 22:07:38 +0200 (Tue, 24 Sep 2019)
Log Message:
-----------
pythontex (24sep19)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/latex/pythontex/NEWS.rst
    trunk/Master/texmf-dist/doc/latex/pythontex/README
    trunk/Master/texmf-dist/doc/latex/pythontex/pythontex.pdf
    trunk/Master/texmf-dist/scripts/pythontex/depythontex2.py
    trunk/Master/texmf-dist/scripts/pythontex/depythontex3.py
    trunk/Master/texmf-dist/scripts/pythontex/pythontex2.py
    trunk/Master/texmf-dist/scripts/pythontex/pythontex3.py
    trunk/Master/texmf-dist/scripts/pythontex/pythontex_engines.py
    trunk/Master/texmf-dist/source/latex/pythontex/pythontex.dtx
    trunk/Master/texmf-dist/source/latex/pythontex/pythontex.ins
    trunk/Master/texmf-dist/tex/latex/pythontex/pythontex.sty

Modified: trunk/Master/texmf-dist/doc/latex/pythontex/NEWS.rst
===================================================================
--- trunk/Master/texmf-dist/doc/latex/pythontex/NEWS.rst	2019-09-24 20:07:21 UTC (rev 52173)
+++ trunk/Master/texmf-dist/doc/latex/pythontex/NEWS.rst	2019-09-24 20:07:38 UTC (rev 52174)
@@ -7,7 +7,58 @@
 ===============
 
 
+v0.17 (2019/09/22)
+------------------
 
+*  Pygments syntax highlighting for the Python console (``pycon`` lexer) now
+   uses the ``python3`` option, and the default Python lexer is now
+   ``python3`` (#156).
+
+*  Added support for JavaScript (#147; thanks to Nathan Carter).
+
+*  Updated Julia support for Julia versions 0.6 (#107), and 0.7 and 1.0 (#126,
+   #130).
+
+*  There are now meaningful error messages for the Julia console when Weave.jl
+   is not installed or raises errors (#131).
+
+*  ``pythontexcustomcode`` and ``\pythontexcustomc`` now set
+   ``pytex.context`` (#65).
+
+*  Added support for R.  The ``R`` family of commands and environments
+   (``\R``, ``\Rc``, ``Rcode``, ...) executes code as a script.
+   There is currently no utilities class or equivalent.  The ``Rcon`` family
+   (``Rconsole``) executes code to emulate an interactive R session (#121).
+
+*  ``fancyvrb`` settings from ``\setpythontexfv`` and console
+   environments now work with Julia and R consoles.
+
+*  ``pythontexcustomcode`` now works with ``juliacon``.  There are now proper
+   ``juliaconcode`` and ``Rconcode`` environments that execute code but
+   typeset nothing, to parallel ``pyconcode`` (#134).
+
+*  Added support for Perl with the ``perl`` and ``pl`` families of commands
+   and environments.  There is currently no utilities class or equivalent.
+
+*  Added support for Perl 6 with the ``perlsix`` and ``psix`` families of
+   commands and environments (#104).  There is currently no utilities class or
+   equivalent.
+
+*  Updated Rust support by using ``dyn`` with traits in utilities object.
+
+*  Under Windows, capitalization of script paths in ``stderr`` is now
+   preserved.
+
+*  Fixed a bug that prevented the ``sub`` environment from working with
+   ``depythontex`` (#155).
+
+*  Fixed a bug in checking mtime of dependencies to see if they have been
+   modified while ``pythontex`` is running.  The check failed for dependencies
+   that do not exist or were deleted before ``pythontex`` can read them
+   (#136).
+
+
+
 v0.16 (2017/07/20)
 ------------------
 

Modified: trunk/Master/texmf-dist/doc/latex/pythontex/README
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/pythontex/pythontex.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/scripts/pythontex/depythontex2.py
===================================================================
--- trunk/Master/texmf-dist/scripts/pythontex/depythontex2.py	2019-09-24 20:07:21 UTC (rev 52173)
+++ trunk/Master/texmf-dist/scripts/pythontex/depythontex2.py	2019-09-24 20:07:38 UTC (rev 52174)
@@ -86,7 +86,7 @@
 
 # Script parameters
 # Version
-__version__ = '0.16'
+__version__ = '0.17'
 
 
 # Functions and parameters for customizing the script output

Modified: trunk/Master/texmf-dist/scripts/pythontex/depythontex3.py
===================================================================
--- trunk/Master/texmf-dist/scripts/pythontex/depythontex3.py	2019-09-24 20:07:21 UTC (rev 52173)
+++ trunk/Master/texmf-dist/scripts/pythontex/depythontex3.py	2019-09-24 20:07:38 UTC (rev 52174)
@@ -86,7 +86,7 @@
 
 # Script parameters
 # Version
-__version__ = '0.16'
+__version__ = '0.17'
 
 
 # Functions and parameters for customizing the script output

Modified: trunk/Master/texmf-dist/scripts/pythontex/pythontex2.py
===================================================================
--- trunk/Master/texmf-dist/scripts/pythontex/pythontex2.py	2019-09-24 20:07:21 UTC (rev 52173)
+++ trunk/Master/texmf-dist/scripts/pythontex/pythontex2.py	2019-09-24 20:07:38 UTC (rev 52174)
@@ -13,7 +13,7 @@
 
 Licensed under the BSD 3-Clause License:
 
-Copyright (c) 2012-2017, Geoffrey M. Poore
+Copyright (c) 2012-2019, Geoffrey M. Poore
 
 All rights reserved.
 
@@ -62,6 +62,7 @@
 from pythontex_engines import *
 import textwrap
 import platform
+import itertools
 
 if sys.version_info[0] == 2:
     try:
@@ -77,7 +78,7 @@
 
 # Script parameters
 # Version
-__version__ = '0.16'
+__version__ = '0.17'
 
 
 
@@ -1213,9 +1214,13 @@
         # Must double-escape any backslashes so that they survive `shlex.split()`
         script = basename
         if os.path.isabs(os.path.expanduser(os.path.normcase(outputdir))):
-            script_full = os.path.expanduser(os.path.normcase(os.path.join(outputdir, basename)))
+            script_full = os.path.expanduser(os.path.join(outputdir, basename))
         else:
-            script_full = os.path.expanduser(os.path.normcase(os.path.join(orig_cwd, outputdir, basename)))
+            script_full = os.path.expanduser(os.path.join(orig_cwd, outputdir, basename))
+        if platform.system() == 'Windows':
+            script_full = script_full.replace('/', '\\')
+        else:
+            script_full = script_full.replace('\\', '/')
         # `shlex.split()` only works with Unicode after 2.7.2
         if (sys.version_info.major == 2 and sys.version_info.micro < 3):
             exec_cmd = shlex.split(bytes(command.format(file=script.replace('\\', '\\\\'), File=script_full.replace('\\', '\\\\'))))
@@ -1275,7 +1280,11 @@
     for key in code_dict:
         family = key.split('#')[0]
         # Uncomment the following for debugging, and comment out what follows
-        '''run_code(encoding, outputdir, workingdir, code_dict[key],
+        '''run_code(encoding, outputdir,
+                                                 workingdir,
+                                                 cc_dict_begin[family],
+                                                 code_dict[key],
+                                                 cc_dict_end[family],
                                                  engine_dict[family].language,
                                                  engine_dict[family].commands,
                                                  engine_dict[family].created,
@@ -1287,9 +1296,12 @@
                                                  engine_dict[family].linenumbers,
                                                  engine_dict[family].lookbehind,
                                                  keeptemps, hashdependencies,
-                                                 pygments_settings)'''
+                                                 pygments_settings]))'''
         tasks.append(pool.apply_async(run_code, [encoding, outputdir,
-                                                 workingdir, code_dict[key],
+                                                 workingdir,
+                                                 cc_dict_begin[family],
+                                                 code_dict[key],
+                                                 cc_dict_end[family],
                                                  engine_dict[family].language,
                                                  engine_dict[family].commands,
                                                  engine_dict[family].created,
@@ -1412,7 +1424,7 @@
     unresolved_sessions = []
     for key in dependencies:
         for dep, val in dependencies[key].items():
-            if val[0] > start_time:
+            if val[0] is None or val[0] > start_time:
                 unresolved_dependencies = True
                 dependencies[key][dep] = (None, None)
                 unresolved_sessions.append(key.replace('#', ':'))
@@ -1474,7 +1486,8 @@
 
 
 
-def run_code(encoding, outputdir, workingdir, code_list, language, commands,
+def run_code(encoding, outputdir, workingdir,
+             cc_begin_list, code_list, cc_end_list, language, commands,
              command_created, extension, makestderr, stderrfilename,
              code_index, errorsig, warningsig, linesig, stderrlookbehind,
              keeptemps, hashdependencies, pygments_settings):
@@ -1512,11 +1525,19 @@
     err_file_name = os.path.expanduser(os.path.normcase(os.path.join(outputdir, basename + '.err')))
     out_file = open(out_file_name, 'w', encoding=encoding)
     err_file = open(err_file_name, 'w', encoding=encoding)
-    script = os.path.expanduser(os.path.normcase(os.path.join(outputdir, basename)))
+    script = os.path.expanduser(os.path.join(outputdir, basename))
+    if platform.system() == 'Windows':
+        script = script.replace('/', '\\')
+    else:
+        script = script.replace('\\', '/')
     if os.path.isabs(script):
         script_full = script
     else:
-        script_full = os.path.expanduser(os.path.normcase(os.path.join(os.getcwd(), outputdir, basename)))
+        script_full = os.path.expanduser(os.path.join(os.getcwd(), outputdir, basename))
+        if platform.system() == 'Windows':
+            script_full = script_full.replace('/', '\\')
+        else:
+            script_full = script_full.replace('\\', '/')
     # #### Need to revise so that intermediate files can be detected and cleaned up
     for f in command_created:
         files.append(f.format(file=script, File=script_full))
@@ -1535,7 +1556,10 @@
         # Add any created files due to the command
         # This needs to be done before attempts to execute, to prevent orphans
         try:
-            proc = subprocess.Popen(exec_cmd, stdout=out_file, stderr=err_file)
+            if family != 'Rcon':
+                proc = subprocess.Popen(exec_cmd, stdout=out_file, stderr=err_file)
+            else:
+                proc = subprocess.Popen(exec_cmd, stdout=out_file, stderr=subprocess.STDOUT)
         except WindowsError as e:
             if e.errno == 2:
                 # Batch files won't be found when called without extension. They
@@ -1546,7 +1570,10 @@
                 # under Windows; a list is not required.
                 exec_cmd_string = ' '.join(exec_cmd)
                 exec_cmd_string = 'cmd /C "@echo off & call {0} & if errorlevel 1 exit 1"'.format(exec_cmd_string)
-                proc = subprocess.Popen(exec_cmd_string, stdout=out_file, stderr=err_file)
+                if family != 'Rcon':
+                    proc = subprocess.Popen(exec_cmd_string, stdout=out_file, stderr=err_file)
+                else:
+                    proc = subprocess.Popen(exec_cmd_string, stdout=out_file, stderr=subprocess.STDOUT)
             else:
                 raise
 
@@ -1567,15 +1594,21 @@
         messages.append('* PythonTeX error')
         messages.append('    Missing output file for ' + key_run.replace('#', ':'))
         errors += 1
+    elif family == 'juliacon' and proc.returncode == 1:
+        messages.append('* PythonTeX error')
+        messages.append('    Running code for Julia console failed')
+        with open(err_file_name, encoding='utf8') as f:
+            messages.append(f.read())
+        errors += 1
     else:
         if family == 'juliacon':
             with open(out_file_name.rsplit('.', 1)[0] + '.tex', 'r', encoding=encoding) as f:
                 tex_data_lines = f.readlines()
-            inst = 0
+            code_iter = itertools.chain(cc_begin_list, code_list, cc_end_list)
             for n, line in enumerate(tex_data_lines):
                 if line.rstrip() == '\\begin{juliaterm}':
-                    tex_data_lines[n] = '=>PYTHONTEX:STDOUT#{0}#code#\n'.format(inst)
-                    inst += 1
+                    c = next(code_iter)
+                    tex_data_lines[n] = '=>PYTHONTEX:STDOUT#{0}#code#\n'.format(c.instance)
                     if n != 0:
                         tex_data_lines[n-1] = ''
                 if line.rstrip() == '\\end{juliaterm}':
@@ -1583,6 +1616,29 @@
             tex_data_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
             with open(out_file_name, 'w', encoding=encoding) as f:
                 f.write(''.join(tex_data_lines))
+        elif family == 'Rcon':
+            with open(out_file_name, 'r', encoding=encoding) as f:
+                stdout_lines = f.readlines()
+            for n, line in enumerate(stdout_lines):
+                if line.startswith('> =>PYTHONTEX:'):
+                    stdout_lines[n] = line[2:]
+                elif '> write("=>PYTHONTEX:' in line:
+                    if line.startswith('> write("=>PYTHONTEX:'):
+                        stdout_lines[n] = ''
+                    else:
+                        # cat() and similar functions can result in the
+                        # prompt not being at the start of a new line.  In
+                        # that case, preserve the prompt to accurately
+                        # emulate the console.  If there is a following
+                        # console environment, this effectively amounts
+                        # to adding an extra empty line (pressing ENTER)
+                        # between the two.
+                        stdout_lines[n] = line.split('write("=>PYTHONTEX:', 1)[0]
+            while stdout_lines and (stdout_lines[-1].startswith('>') and not stdout_lines[-1][1:].strip(' \n')):
+                stdout_lines.pop()
+            stdout_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
+            with open(out_file_name, 'w', encoding=encoding) as f:
+                f.write(''.join(stdout_lines))
 
         f = open(out_file_name, 'r', encoding=encoding)
         out = f.read()
@@ -1640,7 +1696,7 @@
                 else:
                     dependencies[dep] = (os.path.getmtime(dep_file), '')
 
-            if family == 'juliacon':
+            if family in ('juliacon', 'Rcon'):
                 from pygments import highlight
                 from pygments.lexers import get_lexer_by_name
                 from pygments.formatters import LatexFormatter
@@ -1655,16 +1711,21 @@
             for block in out.split('=>PYTHONTEX:STDOUT#')[1:]:
                 if block:
                     delims, content = block.split('#\n', 1)
+                    if content and not content.endswith('\n'):
+                        # Content might not end with a newline.  For example,
+                        # Rcon with something like cat() as the last function.
+                        content += '\n'
                     instance, command = delims.split('#')
                     if content or command in ('s', 'sub'):
                         if instance.endswith('CC'):
-                            messages.append('* PythonTeX warning')
-                            messages.append('    Custom code for "' + family + '" attempted to print or write to stdout')
-                            messages.append('    This is not supported; use a normal code command or environment')
-                            messages.append('    The following content was written:')
-                            messages.append('')
-                            messages.extend(['    ' + l for l in content.splitlines()])
-                            warnings += 1
+                            if family not in ('juliacon', 'Rcon'):
+                                messages.append('* PythonTeX warning')
+                                messages.append('    Custom code for "' + family + '" attempted to print or write to stdout')
+                                messages.append('    This is not supported; use a normal code command or environment')
+                                messages.append('    The following content was written:')
+                                messages.append('')
+                                messages.extend(['    ' + l for l in content.splitlines()])
+                                warnings += 1
                         elif command == 'i':
                             content = r'\pytx at SVMCR{pytx at MCR@' + key_run.replace('#', '@') + '@' + instance + '}\n' + content.rstrip('\n') + '\\endpytx at SVMCR\n\n'
                             macros.append(content)
@@ -1682,7 +1743,7 @@
                                     # Remove newline added by printing, prevent
                                     # LaTeX from adding a space after content
                                     content = content.rsplit('\n', 1)[0] + '\\endinput\n'
-                            if family == 'juliacon':
+                            if family in ('juliacon', 'Rcon'):
                                 content = highlight(content, lexer[family], formatter[family])
                             f.write(content)
                             f.close()
@@ -1693,7 +1754,7 @@
         messages.append('* PythonTeX error')
         messages.append('    Missing stderr file for ' + key_run.replace('#', ':'))
         errors += 1
-    elif family == 'juliacon':
+    elif family in ('juliacon', 'Rcon'):
         pass
     else:
         # Open error and code files.
@@ -1733,7 +1794,7 @@
             index_next = index_now
             start_errgobble = None
             for n, line in enumerate(err_ud):
-                if basename in line:
+                if basename in line and (family not in ('perlsix', 'psix') or '.p6:' in line or '.p6 line' in line):
                     # Get the gobbleation.  This is used to determine if
                     # other lines containing the basename are a continuation,
                     # or separate messages.
@@ -1779,7 +1840,7 @@
                                 # both the error and warning patterns, default to
                                 # error.
                                 past_line = err_ud[index]
-                                if (index < n and basename in past_line):
+                                if (index < n and basename in past_line and (family not in ('perlsix', 'psix') or '.p6:' in past_line or '.p6 line' in past_line)):
                                     break
                                 for pattern in warningsig:
                                     if pattern in past_line:
@@ -1843,8 +1904,9 @@
                 index_now_last = index_now
                 index_next_last = index_next
                 err_key_last_int = -1
+                p6_sorry_search = False
                 for n, line in enumerate(err_ud):
-                    if basename in line:
+                    if basename in line and (family not in ('perlsix', 'psix') or '.p6:' in line or '.p6 line' in line):
                         # Determine the corresponding line number in the document
                         found = False
                         for pattern in linesig:
@@ -1906,6 +1968,35 @@
                                 line = line.replace(fullbasename + '.' + extension, '<file>')
                             elif stderrfilename == 'genericscript':
                                 line = line.replace(fullbasename + '.' + extension, '<script>')
+                            if family in ('perlsix', 'psix'):
+                                # Perl 6 "SORRY!" errors during compiling
+                                # (before execution) need special processing,
+                                # since they lack stderr delims and must
+                                # include lines before the current one.
+                                if p6_sorry_search:  # Already handled
+                                    pass
+                                else:
+                                    p6_sorry_search = True
+                                    p6_sorry_index = n - 1
+                                    while p6_sorry_index >= 0:
+                                        if not err_ud[p6_sorry_index].startswith('===SORRY!==='):
+                                            p6_sorry_index -= 1
+                                            continue
+                                        if errlinenum > index_now[1].lines_total + index_now[1].lines_input:
+                                            p6_linenum_offset = index_now[1].lines_total
+                                        else:
+                                            p6_linenum_offset = index_now[1].lines_total - index_now[1].lines_user + index_now[1].inline_count
+                                        p6_preceding_err_lines = [sub(r'line ([1-9][0-9]*)', lambda m: 'line {0}'.format(int(m.group(1)) - p6_linenum_offset), x) for x in err_ud[p6_sorry_index:n]]
+                                        if stderrfilename == 'full':
+                                            p6_preceding_err_lines[0] = p6_preceding_err_lines[0].replace(fullbasename, basename)
+                                        elif stderrfilename == 'session':
+                                            p6_preceding_err_lines[0] = p6_preceding_err_lines[0].replace(fullbasename, session)
+                                        elif stderrfilename == 'genericfile':
+                                            p6_preceding_err_lines[0] = p6_preceding_err_lines[0].replace(fullbasename + '.' + extension, '<file>')
+                                        elif stderrfilename == 'genericscript':
+                                            p6_preceding_err_lines[0] = p6_preceding_err_lines[0].replace(fullbasename + '.' + extension, '<script>')
+                                        err_dict[err_key].extend(p6_preceding_err_lines)
+                                        break
                             err_dict[err_key].append(line)
                     elif process:
                         err_dict[err_key].append(line)
@@ -1967,7 +2058,7 @@
                     # Never process delimiting info until it is used
                     # Rather, store the index of the last delimiter
                     last_delim = line
-                elif basename in line:
+                elif basename in line and (family not in ('perlsix', 'psix') or '.p6:' in line or '.p6 line' in line):
                     found_basename = True
                     # Get the gobbleation.  This is used to determine if
                     # other lines containing the basename are a continuation,
@@ -2122,7 +2213,7 @@
                         else:
                             process = True
                             err_key = basename + '_' + instance
-                    elif process and basename in line:
+                    elif process and basename in line and (family not in ('perlsix', 'psix') or '.p6:' in line or '.p6 line' in line):
                         found = False
                         for pattern in linesig:
                             try:
@@ -2297,6 +2388,8 @@
         if codetype != ':GLOBAL':
             p = pygments_settings[codetype]['formatter_options'].copy()
             p['commandprefix'] = 'PYG'
+            if pygments_settings[codetype]['lexer'] == 'pycon':
+                p['python3'] = True
             formatter[codetype] = LatexFormatter(**p)
             lexer[codetype] = get_lexer_by_name(pygments_settings[codetype]['lexer'], **p)
 
@@ -2503,6 +2596,8 @@
         from pygments.formatters import LatexFormatter
         p = pygments_settings['formatter_options'].copy()
         p['commandprefix'] = 'PYG'
+        if pygments_settings['lexer'] == 'pycon':
+            p['python3'] = True
         formatter = LatexFormatter(**p)
         lexer = get_lexer_by_name(pygments_settings['lexer'], **p)
     else:

Modified: trunk/Master/texmf-dist/scripts/pythontex/pythontex3.py
===================================================================
--- trunk/Master/texmf-dist/scripts/pythontex/pythontex3.py	2019-09-24 20:07:21 UTC (rev 52173)
+++ trunk/Master/texmf-dist/scripts/pythontex/pythontex3.py	2019-09-24 20:07:38 UTC (rev 52174)
@@ -13,7 +13,7 @@
 
 Licensed under the BSD 3-Clause License:
 
-Copyright (c) 2012-2017, Geoffrey M. Poore
+Copyright (c) 2012-2019, Geoffrey M. Poore
 
 All rights reserved.
 
@@ -62,6 +62,7 @@
 from pythontex_engines import *
 import textwrap
 import platform
+import itertools
 
 if sys.version_info[0] == 2:
     try:
@@ -77,7 +78,7 @@
 
 # Script parameters
 # Version
-__version__ = '0.16'
+__version__ = '0.17'
 
 
 
@@ -1213,9 +1214,13 @@
         # Must double-escape any backslashes so that they survive `shlex.split()`
         script = basename
         if os.path.isabs(os.path.expanduser(os.path.normcase(outputdir))):
-            script_full = os.path.expanduser(os.path.normcase(os.path.join(outputdir, basename)))
+            script_full = os.path.expanduser(os.path.join(outputdir, basename))
         else:
-            script_full = os.path.expanduser(os.path.normcase(os.path.join(orig_cwd, outputdir, basename)))
+            script_full = os.path.expanduser(os.path.join(orig_cwd, outputdir, basename))
+        if platform.system() == 'Windows':
+            script_full = script_full.replace('/', '\\')
+        else:
+            script_full = script_full.replace('\\', '/')
         # `shlex.split()` only works with Unicode after 2.7.2
         if (sys.version_info.major == 2 and sys.version_info.micro < 3):
             exec_cmd = shlex.split(bytes(command.format(file=script.replace('\\', '\\\\'), File=script_full.replace('\\', '\\\\'))))
@@ -1275,7 +1280,11 @@
     for key in code_dict:
         family = key.split('#')[0]
         # Uncomment the following for debugging, and comment out what follows
-        '''run_code(encoding, outputdir, workingdir, code_dict[key],
+        '''run_code(encoding, outputdir,
+                                                 workingdir,
+                                                 cc_dict_begin[family],
+                                                 code_dict[key],
+                                                 cc_dict_end[family],
                                                  engine_dict[family].language,
                                                  engine_dict[family].commands,
                                                  engine_dict[family].created,
@@ -1287,9 +1296,12 @@
                                                  engine_dict[family].linenumbers,
                                                  engine_dict[family].lookbehind,
                                                  keeptemps, hashdependencies,
-                                                 pygments_settings)'''
+                                                 pygments_settings]))'''
         tasks.append(pool.apply_async(run_code, [encoding, outputdir,
-                                                 workingdir, code_dict[key],
+                                                 workingdir,
+                                                 cc_dict_begin[family],
+                                                 code_dict[key],
+                                                 cc_dict_end[family],
                                                  engine_dict[family].language,
                                                  engine_dict[family].commands,
                                                  engine_dict[family].created,
@@ -1412,7 +1424,7 @@
     unresolved_sessions = []
     for key in dependencies:
         for dep, val in dependencies[key].items():
-            if val[0] > start_time:
+            if val[0] is None or val[0] > start_time:
                 unresolved_dependencies = True
                 dependencies[key][dep] = (None, None)
                 unresolved_sessions.append(key.replace('#', ':'))
@@ -1474,7 +1486,8 @@
 
 
 
-def run_code(encoding, outputdir, workingdir, code_list, language, commands,
+def run_code(encoding, outputdir, workingdir,
+             cc_begin_list, code_list, cc_end_list, language, commands,
              command_created, extension, makestderr, stderrfilename,
              code_index, errorsig, warningsig, linesig, stderrlookbehind,
              keeptemps, hashdependencies, pygments_settings):
@@ -1512,11 +1525,19 @@
     err_file_name = os.path.expanduser(os.path.normcase(os.path.join(outputdir, basename + '.err')))
     out_file = open(out_file_name, 'w', encoding=encoding)
     err_file = open(err_file_name, 'w', encoding=encoding)
-    script = os.path.expanduser(os.path.normcase(os.path.join(outputdir, basename)))
+    script = os.path.expanduser(os.path.join(outputdir, basename))
+    if platform.system() == 'Windows':
+        script = script.replace('/', '\\')
+    else:
+        script = script.replace('\\', '/')
     if os.path.isabs(script):
         script_full = script
     else:
-        script_full = os.path.expanduser(os.path.normcase(os.path.join(os.getcwd(), outputdir, basename)))
+        script_full = os.path.expanduser(os.path.join(os.getcwd(), outputdir, basename))
+        if platform.system() == 'Windows':
+            script_full = script_full.replace('/', '\\')
+        else:
+            script_full = script_full.replace('\\', '/')
     # #### Need to revise so that intermediate files can be detected and cleaned up
     for f in command_created:
         files.append(f.format(file=script, File=script_full))
@@ -1535,7 +1556,10 @@
         # Add any created files due to the command
         # This needs to be done before attempts to execute, to prevent orphans
         try:
-            proc = subprocess.Popen(exec_cmd, stdout=out_file, stderr=err_file)
+            if family != 'Rcon':
+                proc = subprocess.Popen(exec_cmd, stdout=out_file, stderr=err_file)
+            else:
+                proc = subprocess.Popen(exec_cmd, stdout=out_file, stderr=subprocess.STDOUT)
         except WindowsError as e:
             if e.errno == 2:
                 # Batch files won't be found when called without extension. They
@@ -1546,7 +1570,10 @@
                 # under Windows; a list is not required.
                 exec_cmd_string = ' '.join(exec_cmd)
                 exec_cmd_string = 'cmd /C "@echo off & call {0} & if errorlevel 1 exit 1"'.format(exec_cmd_string)
-                proc = subprocess.Popen(exec_cmd_string, stdout=out_file, stderr=err_file)
+                if family != 'Rcon':
+                    proc = subprocess.Popen(exec_cmd_string, stdout=out_file, stderr=err_file)
+                else:
+                    proc = subprocess.Popen(exec_cmd_string, stdout=out_file, stderr=subprocess.STDOUT)
             else:
                 raise
 
@@ -1567,15 +1594,21 @@
         messages.append('* PythonTeX error')
         messages.append('    Missing output file for ' + key_run.replace('#', ':'))
         errors += 1
+    elif family == 'juliacon' and proc.returncode == 1:
+        messages.append('* PythonTeX error')
+        messages.append('    Running code for Julia console failed')
+        with open(err_file_name, encoding='utf8') as f:
+            messages.append(f.read())
+        errors += 1
     else:
         if family == 'juliacon':
             with open(out_file_name.rsplit('.', 1)[0] + '.tex', 'r', encoding=encoding) as f:
                 tex_data_lines = f.readlines()
-            inst = 0
+            code_iter = itertools.chain(cc_begin_list, code_list, cc_end_list)
             for n, line in enumerate(tex_data_lines):
                 if line.rstrip() == '\\begin{juliaterm}':
-                    tex_data_lines[n] = '=>PYTHONTEX:STDOUT#{0}#code#\n'.format(inst)
-                    inst += 1
+                    c = next(code_iter)
+                    tex_data_lines[n] = '=>PYTHONTEX:STDOUT#{0}#code#\n'.format(c.instance)
                     if n != 0:
                         tex_data_lines[n-1] = ''
                 if line.rstrip() == '\\end{juliaterm}':
@@ -1583,6 +1616,29 @@
             tex_data_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
             with open(out_file_name, 'w', encoding=encoding) as f:
                 f.write(''.join(tex_data_lines))
+        elif family == 'Rcon':
+            with open(out_file_name, 'r', encoding=encoding) as f:
+                stdout_lines = f.readlines()
+            for n, line in enumerate(stdout_lines):
+                if line.startswith('> =>PYTHONTEX:'):
+                    stdout_lines[n] = line[2:]
+                elif '> write("=>PYTHONTEX:' in line:
+                    if line.startswith('> write("=>PYTHONTEX:'):
+                        stdout_lines[n] = ''
+                    else:
+                        # cat() and similar functions can result in the
+                        # prompt not being at the start of a new line.  In
+                        # that case, preserve the prompt to accurately
+                        # emulate the console.  If there is a following
+                        # console environment, this effectively amounts
+                        # to adding an extra empty line (pressing ENTER)
+                        # between the two.
+                        stdout_lines[n] = line.split('write("=>PYTHONTEX:', 1)[0]
+            while stdout_lines and (stdout_lines[-1].startswith('>') and not stdout_lines[-1][1:].strip(' \n')):
+                stdout_lines.pop()
+            stdout_lines.append('=>PYTHONTEX:DEPENDENCIES#\n=>PYTHONTEX:CREATED#\n')
+            with open(out_file_name, 'w', encoding=encoding) as f:
+                f.write(''.join(stdout_lines))
 
         f = open(out_file_name, 'r', encoding=encoding)
         out = f.read()
@@ -1640,7 +1696,7 @@
                 else:
                     dependencies[dep] = (os.path.getmtime(dep_file), '')
 
-            if family == 'juliacon':
+            if family in ('juliacon', 'Rcon'):
                 from pygments import highlight
                 from pygments.lexers import get_lexer_by_name
                 from pygments.formatters import LatexFormatter
@@ -1655,16 +1711,21 @@
             for block in out.split('=>PYTHONTEX:STDOUT#')[1:]:
                 if block:
                     delims, content = block.split('#\n', 1)
+                    if content and not content.endswith('\n'):
+                        # Content might not end with a newline.  For example,
+                        # Rcon with something like cat() as the last function.
+                        content += '\n'
                     instance, command = delims.split('#')
                     if content or command in ('s', 'sub'):
                         if instance.endswith('CC'):
-                            messages.append('* PythonTeX warning')
-                            messages.append('    Custom code for "' + family + '" attempted to print or write to stdout')
-                            messages.append('    This is not supported; use a normal code command or environment')
-                            messages.append('    The following content was written:')
-                            messages.append('')
-                            messages.extend(['    ' + l for l in content.splitlines()])
-                            warnings += 1
+                            if family not in ('juliacon', 'Rcon'):
+                                messages.append('* PythonTeX warning')
+                                messages.append('    Custom code for "' + family + '" attempted to print or write to stdout')
+                                messages.append('    This is not supported; use a normal code command or environment')
+                                messages.append('    The following content was written:')
+                                messages.append('')
+                                messages.extend(['    ' + l for l in content.splitlines()])
+                                warnings += 1
                         elif command == 'i':
                             content = r'\pytx at SVMCR{pytx at MCR@' + key_run.replace('#', '@') + '@' + instance + '}\n' + content.rstrip('\n') + '\\endpytx at SVMCR\n\n'
                             macros.append(content)
@@ -1682,7 +1743,7 @@
                                     # Remove newline added by printing, prevent
                                     # LaTeX from adding a space after content
                                     content = content.rsplit('\n', 1)[0] + '\\endinput\n'
-                            if family == 'juliacon':
+                            if family in ('juliacon', 'Rcon'):
                                 content = highlight(content, lexer[family], formatter[family])
                             f.write(content)
                             f.close()
@@ -1693,7 +1754,7 @@
         messages.append('* PythonTeX error')
         messages.append('    Missing stderr file for ' + key_run.replace('#', ':'))
         errors += 1
-    elif family == 'juliacon':
+    elif family in ('juliacon', 'Rcon'):
         pass
     else:
         # Open error and code files.
@@ -1733,7 +1794,7 @@
             index_next = index_now
             start_errgobble = None
             for n, line in enumerate(err_ud):
-                if basename in line:
+                if basename in line and (family not in ('perlsix', 'psix') or '.p6:' in line or '.p6 line' in line):
                     # Get the gobbleation.  This is used to determine if
                     # other lines containing the basename are a continuation,
                     # or separate messages.
@@ -1779,7 +1840,7 @@
                                 # both the error and warning patterns, default to
                                 # error.
                                 past_line = err_ud[index]
-                                if (index < n and basename in past_line):
+                                if (index < n and basename in past_line and (family not in ('perlsix', 'psix') or '.p6:' in past_line or '.p6 line' in past_line)):
                                     break
                                 for pattern in warningsig:
                                     if pattern in past_line:
@@ -1843,8 +1904,9 @@
                 index_now_last = index_now
                 index_next_last = index_next
                 err_key_last_int = -1
+                p6_sorry_search = False
                 for n, line in enumerate(err_ud):
-                    if basename in line:
+                    if basename in line and (family not in ('perlsix', 'psix') or '.p6:' in line or '.p6 line' in line):
                         # Determine the corresponding line number in the document
                         found = False
                         for pattern in linesig:
@@ -1906,6 +1968,35 @@
                                 line = line.replace(fullbasename + '.' + extension, '<file>')
                             elif stderrfilename == 'genericscript':
                                 line = line.replace(fullbasename + '.' + extension, '<script>')
+                            if family in ('perlsix', 'psix'):
+                                # Perl 6 "SORRY!" errors during compiling
+                                # (before execution) need special processing,
+                                # since they lack stderr delims and must
+                                # include lines before the current one.
+                                if p6_sorry_search:  # Already handled
+                                    pass
+                                else:
+                                    p6_sorry_search = True
+                                    p6_sorry_index = n - 1
+                                    while p6_sorry_index >= 0:
+                                        if not err_ud[p6_sorry_index].startswith('===SORRY!==='):
+                                            p6_sorry_index -= 1
+                                            continue
+                                        if errlinenum > index_now[1].lines_total + index_now[1].lines_input:
+                                            p6_linenum_offset = index_now[1].lines_total
+                                        else:
+                                            p6_linenum_offset = index_now[1].lines_total - index_now[1].lines_user + index_now[1].inline_count
+                                        p6_preceding_err_lines = [sub(r'line ([1-9][0-9]*)', lambda m: 'line {0}'.format(int(m.group(1)) - p6_linenum_offset), x) for x in err_ud[p6_sorry_index:n]]
+                                        if stderrfilename == 'full':
+                                            p6_preceding_err_lines[0] = p6_preceding_err_lines[0].replace(fullbasename, basename)
+                                        elif stderrfilename == 'session':
+                                            p6_preceding_err_lines[0] = p6_preceding_err_lines[0].replace(fullbasename, session)
+                                        elif stderrfilename == 'genericfile':
+                                            p6_preceding_err_lines[0] = p6_preceding_err_lines[0].replace(fullbasename + '.' + extension, '<file>')
+                                        elif stderrfilename == 'genericscript':
+                                            p6_preceding_err_lines[0] = p6_preceding_err_lines[0].replace(fullbasename + '.' + extension, '<script>')
+                                        err_dict[err_key].extend(p6_preceding_err_lines)
+                                        break
                             err_dict[err_key].append(line)
                     elif process:
                         err_dict[err_key].append(line)
@@ -1967,7 +2058,7 @@
                     # Never process delimiting info until it is used
                     # Rather, store the index of the last delimiter
                     last_delim = line
-                elif basename in line:
+                elif basename in line and (family not in ('perlsix', 'psix') or '.p6:' in line or '.p6 line' in line):
                     found_basename = True
                     # Get the gobbleation.  This is used to determine if
                     # other lines containing the basename are a continuation,
@@ -2122,7 +2213,7 @@
                         else:
                             process = True
                             err_key = basename + '_' + instance
-                    elif process and basename in line:
+                    elif process and basename in line and (family not in ('perlsix', 'psix') or '.p6:' in line or '.p6 line' in line):
                         found = False
                         for pattern in linesig:
                             try:
@@ -2297,6 +2388,8 @@
         if codetype != ':GLOBAL':
             p = pygments_settings[codetype]['formatter_options'].copy()
             p['commandprefix'] = 'PYG'
+            if pygments_settings[codetype]['lexer'] == 'pycon':
+                p['python3'] = True
             formatter[codetype] = LatexFormatter(**p)
             lexer[codetype] = get_lexer_by_name(pygments_settings[codetype]['lexer'], **p)
 
@@ -2503,6 +2596,8 @@
         from pygments.formatters import LatexFormatter
         p = pygments_settings['formatter_options'].copy()
         p['commandprefix'] = 'PYG'
+        if pygments_settings['lexer'] == 'pycon':
+            p['python3'] = True
         formatter = LatexFormatter(**p)
         lexer = get_lexer_by_name(pygments_settings['lexer'], **p)
     else:

Modified: trunk/Master/texmf-dist/scripts/pythontex/pythontex_engines.py
===================================================================
--- trunk/Master/texmf-dist/scripts/pythontex/pythontex_engines.py	2019-09-24 20:07:21 UTC (rev 52173)
+++ trunk/Master/texmf-dist/scripts/pythontex/pythontex_engines.py	2019-09-24 20:07:38 UTC (rev 52174)
@@ -17,7 +17,7 @@
 
 
 
-Copyright (c) 2012-2017, Geoffrey M. Poore
+Copyright (c) 2012-2018, Geoffrey M. Poore
 All rights reserved.
 Licensed under the BSD 3-Clause License:
     http://www.opensource.org/licenses/BSD-3-Clause
@@ -33,7 +33,8 @@
 from collections import OrderedDict, namedtuple
 
 
-interpreter_dict = {k:k for k in ('python', 'ruby', 'julia', 'octave', 'bash', 'sage', 'rustc')}
+interpreter_dict = {k:k for k in ('python', 'ruby', 'julia', 'octave', 'bash',
+                                  'sage', 'rustc', 'Rscript', 'perl', 'perl6')}
 # The {file} field needs to be replaced by itself, since the actual
 # substitution of the real file can only be done at runtime, whereas the
 # substitution for the interpreter should be done when the engine is
@@ -194,7 +195,7 @@
                 if not isinstance(l, str):
                     raise TypeError('CodeEngine needs "linenumbers" to contain strings')
         # Need to replace tags
-        linenumbers = [l.replace('{number}', r'(\d+)') for l in linenumbers]
+        linenumbers = [r'(\d+)'.join(re.escape(x) for x in l.split('{number}')) if '{number}' in l else l for l in linenumbers]
         self.linenumbers = linenumbers
 
         # Type check lookbehind
@@ -950,7 +951,7 @@
     # Currently, Julia only supports UTF-8
     # So can't set stdout and stderr encoding
 
-    type JuliaTeXUtils
+    mutable struct JuliaTeXUtils
         id::AbstractString
         family::AbstractString
         session::AbstractString
@@ -983,8 +984,8 @@
         function JuliaTeXUtils()
             self = new()
             self.self = self
-            self._dependencies = Array(AbstractString, 0)
-            self._created = Array(AbstractString, 0)
+            self._dependencies = AbstractString[]
+            self._created = AbstractString[]
             self._context_raw = ""
 
             function formatter(expr)
@@ -1095,7 +1096,7 @@
     jltex.line = "{line}"
 
     println("{stdoutdelim}")
-    write(STDERR, "{stderrdelim}\\n")
+    write(stderr, "{stderrdelim}\\n")
     jltex.before()
 
     {code}
@@ -1267,6 +1268,7 @@
            octave_template, octave_wrapper, 'disp({code})', octave_sub,
            'error', 'warning', 'line {number}')
 
+
 bash_template = '''
     cd "{workingdir}"
     {body}
@@ -1297,7 +1299,7 @@
         use std::{{borrow, collections, fmt, fs, io, iter, ops, path}};
         use self::OpenMode::{{ReadMode, WriteMode, AppendMode, TruncateMode, CreateMode, CreateNewMode}};
         pub struct UserAction<'u> {{
-            _act: Box<FnMut() + 'u>
+            _act: Box<dyn FnMut() + 'u>
         }}
         impl<'u> UserAction<'u> {{
             pub fn new() -> Self {{
@@ -1323,8 +1325,8 @@
         impl<'u, U: Into<UserAction<'u>> + 'u> ops::Add<U> for UserAction<'u> {{
             type Output = UserAction<'u>;
             fn add(self, f: U) -> Self::Output {{
-                let mut self_act: Box<FnMut() + 'u> = self._act;
-                let mut other_act: Box<FnMut() + 'u> = f.into()._act;
+                let mut self_act: Box<dyn FnMut() + 'u> = self._act;
+                let mut other_act: Box<dyn FnMut() + 'u> = f.into()._act;
                 Self::from(move || {{ self_act.as_mut()(); other_act.as_mut()(); }})
             }}
         }}
@@ -1335,7 +1337,7 @@
             }}
         }}
         impl<'u> ops::Deref for UserAction<'u> {{
-            type Target = FnMut() + 'u;
+            type Target = dyn FnMut() + 'u;
             fn deref(&self) -> &Self::Target {{
                 &*self._act
             }}
@@ -1346,7 +1348,7 @@
             }}
         }}
         pub struct RustTeXUtils<'u> {{
-            _formatter: Box<FnMut(&fmt::Display) -> String + 'u>,
+            _formatter: Box<dyn FnMut(&dyn fmt::Display) -> String + 'u>,
             pub before: UserAction<'u>,
             pub after: UserAction<'u>,
             pub family: &'u str,
@@ -1410,7 +1412,7 @@
         impl<'u> RustTeXUtils<'u> {{
             pub fn new() -> Self {{
                 RustTeXUtils {{
-                    _formatter: Box::new(|x: &fmt::Display| format!("{{}}", x)),
+                    _formatter: Box::new(|x: &dyn fmt::Display| format!("{{}}", x)),
                     before: UserAction::new(),
                     after: UserAction::new(),
                     family: "{family}",
@@ -1428,7 +1430,7 @@
             pub fn formatter<A: fmt::Display>(&mut self, x: A) -> String {{
                 (self._formatter)(&x)
             }}
-            pub fn set_formatter<F: FnMut(&fmt::Display) -> String + 'u>(&mut self, f: F) {{
+            pub fn set_formatter<F: FnMut(&dyn fmt::Display) -> String + 'u>(&mut self, f: F) {{
                 self._formatter = Box::new(f);
             }}
             pub fn add_dependencies<SS: IntoIterator>(&mut self, deps: SS)
@@ -1541,3 +1543,208 @@
            created='{File}.exe')
 
 SubCodeEngine('rust', 'rs')
+
+
+r_template = '''
+    library(methods)
+    setwd("{workingdir}")
+    pdf(file=NULL)
+    {body}
+    write("{dependencies_delim}", stdout())
+    write("{created_delim}", stdout())
+    '''
+
+r_wrapper = '''
+    write("{stdoutdelim}", stdout())
+    write("{stderrdelim}", stderr())
+    {code}
+    '''
+
+r_sub = '''
+    write("{field_delim}", stdout())
+    write(toString({field}), stdout())
+    '''
+
+CodeEngine('R', 'R', '.R',
+           '{Rscript} "{file}.R"',
+           r_template, r_wrapper, 'write(toString({code}), stdout())', r_sub,
+           ['error', 'Error'], ['warning', 'Warning'],
+           'line {number}')
+
+
+rcon_template = '''
+    options(echo=TRUE, error=function(){{}})
+    library(methods)
+    setwd("{workingdir}")
+    pdf(file=NULL)
+    {body}
+    '''
+
+rcon_wrapper = '''
+    write("{stdoutdelim}", stdout())
+    {code}
+    '''
+
+CodeEngine('Rcon', 'R', '.R',
+           '{Rscript} "{file}.R"',
+           rcon_template, rcon_wrapper, '', '',
+           ['error', 'Error'], ['warning', 'Warning'],
+           '')
+
+
+perl_template = '''
+    use v5.14;
+    use utf8;
+    use strict;
+    use autodie;
+    use warnings;
+    use warnings qw(FATAL utf8);
+    use feature qw(unicode_strings);
+    use open qw(:encoding(UTF-8) :std);
+    chdir("{workingdir}");
+    {body}
+    print STDOUT "{dependencies_delim}\\n";
+    print STDOUT "{created_delim}\\n";
+    '''
+
+perl_wrapper = '''
+    print STDOUT "{stdoutdelim}\\n";
+    print STDERR "{stderrdelim}\\n";
+    {code}
+    '''
+
+perl_sub = '''
+    print STDOUT "{field_delim}\\n";
+    print STDOUT "" . ({field});
+    '''
+
+CodeEngine('perl', 'perl', '.pl',
+           '{perl} "{file}.pl"',
+           perl_template, perl_wrapper, 'print STDOUT "" . ({code});', perl_sub,
+           ['error', 'Error'], ['warning', 'Warning'],
+           'line {number}')
+
+SubCodeEngine('perl', 'pl')
+
+
+perl6_template = '''
+    use v6;
+    chdir("{workingdir}");
+    {body}
+    put "{dependencies_delim}";
+    put "{created_delim}";
+    '''
+
+perl6_wrapper = '''
+    put "{stdoutdelim}";
+    note "{stderrdelim}";
+    {code}
+    '''
+
+perl6_sub = '''
+    put "{field_delim}";
+    put ({field});
+    '''
+
+CodeEngine('perlsix', 'perl6', '.p6',
+           '{perl6} "{File}.p6"',
+           perl6_template, perl6_wrapper, 'put ({code});', perl6_sub,
+           ['error', 'Error', 'Cannot'], ['warning', 'Warning'],
+           ['.p6:{number}', '.p6 line {number}'], True)
+
+SubCodeEngine('perlsix', 'psix')
+
+javascript_template = '''
+    jstex = {{
+        before : function () {{ }},
+        after : function () {{ }},
+        _dependencies : [ ],
+        _created : [ ],
+        add_dependencies : function () {{
+            jstex._dependencies = jstex._dependencies.concat(
+                Array.prototype.slice.apply( arguments ) );
+        }},
+        add_created : function () {{
+            jstex._created = jstex._created.concat(
+                Array.prototype.slice.apply( arguments ) );
+        }},
+        cleanup : function () {{
+            console.log( "{dependencies_delim}" );
+            jstex._dependencies.map(
+                dep => console.log( dep ) );
+            console.log( "{created_delim}" );
+            jstex._dependencies.map(
+                cre => console.log( cre ) );
+        }},
+        formatter : function ( x ) {{
+            return String( x );
+        }},
+        escape : function ( x ) {{
+            return String( x ).replace( /_/g, '\\\\_' )
+                              .replace( /\\$/g, '\\\\$' )
+                              .replace( /\\^/g, '\\\\^' );
+        }},
+        docdir : process.cwd(),
+        context : {{ }},
+        _context_raw : '',
+        set_context : function ( expr ) {{
+            if ( expr != '' && expr != jstex._context_raw ) {{
+                jstex.context = {{ }};
+                expr.split( ',' ).map( pair => {{
+                    const halves = pair.split( '=' );
+                    jstex.context[halves[0].trim()] = halves[1].trim();
+                }} );
+            }}
+        }}
+    }};
+
+    try {{
+        process.chdir( "{workingdir}" );
+    }} catch ( e ) {{
+        if ( process.argv.indexOf( '--manual' ) == -1 )
+            console.error( e );
+    }}
+    if ( module.paths.indexOf( jstex.docdir ) == -1 )
+        module.paths.unshift( jstex.docdir );
+
+    {extend}
+
+    jstex.id = "{family}_{session}_{restart}";
+    jstex.family = "{family}";
+    jstex.session = "{session}";
+    jstex.restart = "{restart}";
+
+    {body}
+
+    jstex.cleanup();
+    '''
+
+javascript_wrapper = '''
+    jstex.command = "{command}";
+    jstex.set_context( "{context}" );
+    jstex.args = "{args}";
+    jstex.instance = "{instance}";
+    jstex.line = "{line}";
+
+    console.log( "{stdoutdelim}" );
+    console.error( "{stderrdelim}" );
+    jstex.before();
+
+    {code}
+
+    jstex.after();
+    '''
+
+javascript_sub = '''
+    console.log( "{field_delim}" );
+    console.log( {field} );
+    '''
+
+CodeEngine('javascript', 'javascript', '.js',
+           'node "{file}.js"',
+           javascript_template, javascript_wrapper,
+           'console.log( jstex.formatter( {code} ) )',
+           javascript_sub,
+           ['error', 'Error'], ['warning', 'Warning'],
+           ':{number}')
+

Modified: trunk/Master/texmf-dist/source/latex/pythontex/pythontex.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/pythontex/pythontex.dtx	2019-09-24 20:07:21 UTC (rev 52173)
+++ trunk/Master/texmf-dist/source/latex/pythontex/pythontex.dtx	2019-09-24 20:07:38 UTC (rev 52174)
@@ -1,6 +1,6 @@
 % \iffalse meta-comment
 %
-% Copyright (C) 2012-2017 by Geoffrey M. Poore <gpoore at gmail.com>
+% Copyright (C) 2012-2019 by Geoffrey M. Poore <gpoore at gmail.com>
 % ---------------------------------------------------------------------------
 % This work may be distributed and/or modified under the
 % conditions of the LaTeX Project Public License, either version 1.3
@@ -26,7 +26,7 @@
 %<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
 %<package>\ProvidesPackage{pythontex}
 %<*package>
-    [2017/07/20 v0.16 execute and typeset Python code and other languages]
+    [2019/09/22 v0.17 execute and typeset Python code and other languages]
 %</package>
 %
 %<*driver>
@@ -40,6 +40,20 @@
 \usepackage{environ}
 \usepackage{enumitem}
 \usepackage{fvextra}
+% The typesetting for macrocode doesn't use \@noligs, which upquote modifies.
+% So apply the upquote fix to \verbatim at nolig@list as well, which is in macrocode.
+\begingroup
+\catcode`'=\active
+\catcode``=\active
+\g at addto@macro\verbatim at nolig@list{%
+  \let'\textquotesingle
+  \let`\textasciigrave
+  \ifx\encodingdefault\upquote at OTone
+  \ifx\ttdefault\upquote at cmtt
+    \def'{\char13 }%
+    \def`{\char18 }%
+  \fi\fi}
+\endgroup
 \usepackage{pythontex}
 \usepackage{hyperref}
 \hypersetup{
@@ -134,7 +148,7 @@
 %</driver>
 % \fi
 %
-% \CheckSum{3218}
+% \CheckSum{3289}
 %
 % \CharacterTable
 %  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
@@ -153,7 +167,27 @@
 %   Right brace   \}     Tilde         \~}
 %
 %
-
+%
+% \begin{changelog}{v0.17}{2019/09/22}
+% \begin{itemize}
+% \item Pygments syntax highlighting for the Python console (\texttt{pycon} lexer) now uses the \texttt{python3} option, and the default Python lexer is now \texttt{python3} (\#156).
+% \item Added support for JavaScript (\#147; thanks to Nathan Carter).
+% \item Updated Julia support for Julia versions 0.6 (\#107), and 0.7 and 1.0 (\#126, \#130).
+% \item There are now meaningful error messages for the Julia console when Weave.jl is not installed or raises errors (\#131).
+% \item \texttt{pythontexcustomcode} and \texttt{\string\pythontexcustomc} now set \texttt{pytex.context} (\#65).
+% \item Added support for R.  The \texttt{R} family of commands and environments (\texttt{\string\R}, \texttt{\string\Rc}, \texttt{Rcode}, ...) executes code as a script.  There is currently no utilities class or equivalent.  The \texttt{Rcon} family (\texttt{Rconsole}) executes code to emulate an interactive R session (\#121).
+% \item \texttt{fancyvrb} settings from \texttt{\string\setpythontexfv} and console environments now work with Julia and R consoles.
+% \item \texttt{pythontexcustomcode} now works with \texttt{juliacon}.  There are now proper \texttt{juliaconcode} and \texttt{Rconcode} environments that execute code but typeset nothing, to parallel \texttt{pyconcode} (\#134).
+% \item Added support for Perl with the \texttt{perl} and \texttt{pl} families of commands and environments.  There is currently no utilities class or equivalent.
+% \item Added support for Perl 6 with the \texttt{perlsix} and \texttt{psix} families of commands and environments (\#104).  There is currently no utilities class or equivalent.
+% \item Updated Rust support by using \texttt{dyn} with traits in utilities object.
+% \item Under Windows, capitalization of script paths in \texttt{stderr} is now preserved.
+% \item Fixed a bug that prevented the \texttt{sub} environment from working with \texttt{depythontex} (\#155).
+% \item Fixed a bug in checking mtime of dependencies to see if they have been modified while \texttt{pythontex} is running.  The check failed for dependencies that do not exist or were deleted before \texttt{pythontex} can read them (\#136).
+% \end{itemize}
+% \end{changelog}
+%
+%
 % \begin{changelog}{v0.16}{2017/07/20}
 % \begin{itemize}
 % \item Added preliminary console support for Julia (\#98).
@@ -436,8 +470,9 @@
 %
 % Because documents that use \pytex\ mix \LaTeX\ and Python code, they are less suitable than plain \LaTeX\ documents for journal submission, sharing, and conversion to other formats.  \pytex\ includes a |depythontex| utility that creates a copy of a document in which all \pytex\ content is replaced by its output.
 %
-% While Python is the focus of \pytex, adding basic support for an additional language is usually as simple as creating a new class instance and a few templates, usually totaling less than 100 lines of code.  The following languages already have built-in support:  Ruby, Julia, and Octave.
+% While Python is the focus of \pytex, adding basic support for an additional language is usually as simple as creating a new class instance and a few templates, usually totaling less than 100 lines of code.  The following languages already have built-in support:  Ruby, Julia, Octave, Bash, Rust, R, Perl, Perl 6, and JavaScript.
 % \end{abstract}
+% 
 %
 %
 % \section*{\centering Warning}
@@ -660,7 +695,7 @@
 %
 % \DescribeMacro{usefamily=\meta{basename}/\marg{basename1,~basename2,~...}}
 %
-% By default, only the |py|, |sympy|, and |pylab| families of commands and environments are defined, to prevent possible package conflicts.\footnote{For example, a \texttt{\string\ruby} command for Ruby code, and the \texttt{\string\ruby} command defined by the Ruby package in the \href{http://www.ctan.org/pkg/cjk}{CJK package}.}  This option defines preconfigured families for other available languages.  It takes either a single language base name, or a list of comma-separated names enclosed in curly braces.  Currently, the Ruby families |rb| and |ruby|, the Julia families |jl| and |julia|, and the Octave family |octave| may be created.
+% By default, only the |py|, |sympy|, and |pylab| families of commands and environments are defined, to prevent possible package conflicts.\footnote{For example, a \texttt{\string\ruby} command for Ruby code, and the \texttt{\string\ruby} command defined by the Ruby package in the \href{http://www.ctan.org/pkg/cjk}{CJK package}.}  This option defines preconfigured families for other available languages.  It takes either a single language base name, or a list of comma-separated names enclosed in curly braces.  For example, the Ruby families |rb| and |ruby|, the Julia families |jl| and |julia|, and the Octave family |octave| may be enabled.  For a full list of supported languages, see \Cref{sec:other-languages}.
 %
 %
 % \DescribeMacro{gobble=none/auto default:none}
@@ -1519,13 +1554,15 @@
 % \section{Support for additional languages}
 % \label{sec:other-languages}
 %
-% Beginning with v0.12, it is much simpler to add support for languages beyond Python.  Support for several additional languages will be added in coming months.
+% Details about adding support for additional languages are in \Cref{sec:other-languages:adding}.  This section begins with a brief overview of supported languages and available features.
 %
-% In the immediate future, support for additional languages will be part of \pytex.  Later, it may make sense to provide an alternative interface for other languages.  For example, a package could be created that provides access to \pytex\ internals in a language-agnostic manner, without having the word ``python'' as part of the command names.
+% Languages beyond Python are typically not be enabled by default to prevent potential macro naming conflicts with other packages.  Languages are enabled via the |usefamily| package option (\Cref{sec:usage}).  For example,
+%\begin{verbatim}
+%\usepackage[usefamily=ruby]{pythontex}
+%\end{verbatim}
+% Usually at least two possible base names for commands and environments will be provided for each language.  Typically these will be the name of the language and the language's file extension.  For example, Ruby has the |ruby| and |rb| base names.  You can choose which base name to use for creating a family of commands and environments based on personal preference and potential naming conflicts.
 %
-% Languages beyond Python will typically not be enabled by default, to prevent potential macro naming conflicts with other packages.  At least two possible base names for commands and environments will be provided for each language.  Typically these will be the name of the language and the language's file extension.  For example, Ruby has the |ruby| and |rb| base names.  You can choose which base name to use for creating a family of commands and environments based on personal preference and potential naming conflicts.
 %
-%
 % \subsection{Ruby}
 %
 % Support for Ruby was added in v0.12.  Ruby support should be almost at the same level as that for Python.
@@ -1549,7 +1586,7 @@
 %
 % \subsubsection*{Console}
 %
-% Preliminary Julia console support was added in v0.16, under the base name |juliacon|.  Unlike the Python console environments that allow invalid input, currently everything entered in a |juliaconsole| environment must be valid.  The Julia console uses \href{https://github.com/mpastell/Weave.jl}{\textsf{Weave.jl}} internally to evaluate code, and it currently does not support invalid input.
+% Julia console support was added in v0.16.  It may be enabled by loading \pytex\ with |usefamily=juliacon|.  The |juliaconsole| environment uses \href{https://github.com/mpastell/Weave.jl}{\textsf{Weave.jl}} internally to evaluate code.  There is also a |juliaconcode| environment that executes code but typesets nothing.
 %
 %
 % \subsection{Octave}
@@ -1587,7 +1624,40 @@
 % Additionally, when using |\rust| and |\rs|, keep in mind that these wrap code in a block, so you \emph{cannot} use |rstex| by value in these contexts (both shared and mutable references are still fine, though).
 %
 %
+% \subsection{R}
+%
+% Support for R was added in v0.17.
+%
+% Loading \pytex\ with |usefamily=R| enables the |R| family of commands and environments (|\R|, |\Rc|, |Rcode|, ...).  These execute code with |Rscript|.  The |methods| library is loaded automatically as part of the template code.  Expressions passed to the |\R| command are converted into strings via |toString()|.  There is currently no utilities class or related features.  A null graphics device, |pdf(file=NULL)|, is created by default to avoid the automatic, unintentional creation of plot files with default names.  Plots that are to be saved require explicit graphics commands.
+%
+% \subsubsection*{Console}
+%
+% Loading \pytex\ with |usefamily=Rcon| enables the |Rconsole| environment, which executes code to emulate an interactive R session.  There is also an |Rconcode| environment that executes code but typesets nothing.  Code is executed with |Rscript|.  The |methods| library is loaded automatically as part of the template code.  The option |echo=TRUE| is used to intersperse code with output, while |error=function(){}| is used to avoid halting on errors.  A null graphics device, |pdf(file=NULL)|, is created by default to avoid the automatic, unintentional creation of plot files with default names.  Plots that are to be saved require explicit graphics commands.
+%
+%
+% \subsection{Perl}
+% 
+% Support for Perl was added in v0.17.
+%
+% Loading \pytex\ with |usefamily=perl| enables the |perl| family of commands and environments.  Alternatively, |usefamily=pl| may be used to enable the |pl| family.  There is currently no utilities class or related features.
+%
+%
+% \subsection{Perl 6}
+% 
+% Support for Perl 6 was added in v0.17.
+%
+% Loading \pytex\ with |usefamily=perlsix| enables the |perlsix| family of commands and environments.  Alternatively, |usefamily=psix| may be used to enable the |psix| family.  There is currently no utilities class or related features.
+%
+%
+% \subsection{JavaScript}
+%
+% Support for JavaScript was added in v0.17.
+%
+% Loading \pytex\ with |usefamily=javascript| enables the |javascript| family of commands and environments.  Alternatively, |usefamily=js| may be used to enable the |js| family.  There is a utilities object |jstex|.
+%
+%
 % \subsection{Adding support for a new language}
+% \label{sec:other-languages:adding}
 %
 % Adding support for an additional language involves creating two templates,  creating a new instance of a class, and using a \pytex\ macro.  In some cases, additional changes may be necessary for full support.  The information below does not deal with creating |console| families; additional support for user-defined |console| families will be added in the future.
 %
@@ -1794,7 +1864,9 @@
 %
 % Thanks to Alexander Altman for suggesting Rust support and providing template code.
 %
+% Thanks to Nathan Carter for suggesting JavaScript support and providing template code.
 %
+%
 % \PrintChangelog
 %
 % \StopEventually{\PrintIndex}
@@ -1813,7 +1885,7 @@
 % We store the name of the package in a macro for later use in warnings and error messages.
 %    \begin{macrocode}
 \newcommand{\pytx at packagename}{PythonTeX}
-\newcommand{\pytx at packageversion}{0.16}
+\newcommand{\pytx at packageversion}{0.17}
 %    \end{macrocode}
 %
 % \subsection{Required packages}
@@ -3349,7 +3421,7 @@
     }%
     \xdef\pytx at type{CC:#2:#1}%
     \edef\pytx at cmd{c}%
-    \def\pytx at context{}%
+    \pytx at SetContext
     \def\pytx at group{none}%
     \let\pytx at InlineShow\@empty
     \let\pytx at InlineSave\pytx at InlineSaveCode
@@ -3683,6 +3755,22 @@
     }%
     {\end{VerbatimOut}%
     \xdef\pytx at stdfile{\pytx at type_\pytx at session_\pytx at group_\arabic{\pytx at counter}}%
+    \ifcsname pytx at nonpyconsole@\pytx at type\endcsname
+        \ifcsname pytx at code@as at console\endcsname
+            \setcounter{FancyVerbLine}{\value{\pytx at linecount}}%
+            \pytx at FVSet
+            \ifdefstring{\pytx at fvopttmp}{}{}{\expandafter\fvset\expandafter{\pytx at fvopttmp}}%
+            \pytx at ConfigPygments
+            \InputIfFileExists{\pytx at outputdir/\pytx at stdfile.stdout}%
+               {\DepyFile{p:\pytx at outputdir/\pytx at stdfile.stdout}}%
+               {\par\textbf{??~\pytx at packagename~??}\par
+                \PackageWarning{\pytx at packagename}{Non-existent console content}}%
+            \setcounter{\pytx at linecount}{\value{FancyVerbLine}}%
+        \else
+        \fi
+        \let\pytx at EnvAutoprint\relax
+    \else
+    \fi
     \setcounter{FancyVerbLine}{\value{pytx at FancyVerbLineTemp}}%
     \stepcounter{\pytx at counter}%
     \pytx at EnvAutoprint
@@ -3699,6 +3787,8 @@
     \ifstrempty{#1}{\edef\pytx at session{default}}{\StrSubstitute{#1}{:}{-}[\pytx at session]}%
     \xdef\pytx at counter{pytx@\pytx at type @\pytx at session @\pytx at group}%
     \pytx at CheckCounter{\pytx at counter}%
+    \edef\pytx at linecount{\pytx at counter @line}%
+    \pytx at CheckCounter{\pytx at linecount}%
     \pytx at WriteCodefileInfo
     \begingroup
     \obeylines
@@ -3712,6 +3802,8 @@
 % As described above, this macro captures a second optional argument, if present, and then starts the |VerbatimOut| environment.  Note that |VerbatimOut| does not have a mandatory argument, because we are invoking our custom |\pytx at FVB@VerbatimOut| macro.  The default |fancyvrb| macro needs an argument to tell it the name of the file to which to save the verbatim content.  But in our case, we are always writing to the same file, and the custom macro accounts for this by not having a mandatory file name argument.  We must perform the typical |FancyVerbLine| trickery, to prevent the |fancyvrb| line counter from being affected by \textbf{writing} content!
 %    \begin{macrocode}
 \def\pytx at BeginCodeEnv@i[#1]{%
+    \def\pytx at fvopttmp{#1}%
+    \def\pytx at argspprint{#1}%
     \setcounter{pytx at FancyVerbLineTemp}{\value{FancyVerbLine}}%
     \let\FVB at VerbatimOut\pytx at FVB@VerbatimOut
     \let\FVE at VerbatimOut\pytx at FVE@VerbatimOut
@@ -3742,7 +3834,7 @@
     }%
     \xdef\pytx at type{CC:#2:#1}%
     \edef\pytx at cmd{code}%
-    \def\pytx at context{}%
+    \pytx at SetContext
     \def\pytx at group{none}%
     \pytx at BeginCodeEnv[none]}%
 {\end{VerbatimOut}%
@@ -3763,7 +3855,7 @@
 \newcommand{\pytx at MakeSubFV}[1]{%
     \expandafter\newenvironment{#1sub}{%
         \VerbatimEnvironment
-        \Depythontex{env:#1sub:om|:p}%
+        \Depythontex{env:#1sub:oo|:p}%
         \xdef\pytx at type{#1}%
         \edef\pytx at cmd{sub}%
         \pytx at SetContext
@@ -4000,6 +4092,34 @@
 %    \end{macrocode}
 % \end{macro}
 %
+%
+% \begin{macro}{\makepythontexfamily at con}
+% This macro creates |console| and |code| environments for non-Python consoles.  \pytex\ was not designed with commands and environments for non-Python consoles.  Non-Python consoles are currently created via specially customized |code| environments.  Note that simply creating these |console| and |code| environments is typically not enough to create non-Python consoles; |pythontex2.py| and |pythontex3.py| usually also require customization.  This macro's definition should not be treated as stable; it will change in the future.  The ultimate long-term goal is to eliminate it entirely, by redesigning the code execution core of \pytex\ to accomodate non-Python consoles more easily.
+%    \begin{macrocode}
+\newcommand{\makepythontexfamily at con}[2][text]{%
+    \pgfkeys{/PYTX/family, name=#2con, default, pyglexer=#1, console=true}%
+    \expandafter\xdef\csname pytx at macroformatter@#2con\endcsname{\pytx at tmp@pprinter}%
+    \expandafter\gdef\csname pytx at fvsettings@#2con\endcsname{}%
+    \expandafter\xdef\csname pytx at pyglexer@#2con\endcsname{\pytx at tmp@pyglexer}%
+    \expandafter\xdef\csname pytx at pygopt@#2con\endcsname{\pytx at tmp@pygopt}%
+    \expandafter\xdef\csname pytx at console@#2con\endcsname{\pytx at tmp@console}%
+    \AtEndDocument{\immediate\write\pytx at codefile{pygfamily=#2con|%
+        \csname pytx at pyglexer@#2con\endcsname|%
+        \csname pytx at pygopt@#2con\endcsname}%
+    }%
+    \pytx at MakeCodeFV{#2con}%
+    \expandafter\global\expandafter\let\csname pytx at nonpyconsole@#2con\endcsname\relax
+    \newenvironment{#2console}%
+       {\VerbatimEnvironment
+        \def\pytx at type{#2con}%
+        \let\pytx at code@as at console\relax
+        \begin{#2concode}}%
+       {\end{#2concode}}%
+}
+%    \end{macrocode}
+% \end{macro}
+
+%
 % \begin{macro}{\setpythontexpyglexer}
 % We need to be able to reset the lexer associated with a family after the family has already been created.
 %    \begin{macrocode}
@@ -4068,11 +4188,11 @@
 %
 % All of these command and environment families are created conditionally, depending on whether the package option |pygments| is used, via |\makepythontexfamily|.  We recommend that any custom families created by the user be constructed in the same manner.
 %    \begin{macrocode}
-\makepythontexfamily[pyglexer=python]{py}
+\makepythontexfamily[pyglexer=python3]{py}
 \makepythontexfamily[pyglexer=pycon, console=true]{pycon}
-\makepythontexfamily[pyglexer=python]{sympy}
+\makepythontexfamily[pyglexer=python3]{sympy}
 \makepythontexfamily[pyglexer=pycon, console=true]{sympycon}
-\makepythontexfamily[pyglexer=python]{pylab}
+\makepythontexfamily[pyglexer=python3]{pylab}
 \makepythontexfamily[pyglexer=pycon, console=true]{pylabcon}
 %    \end{macrocode}
 % We also need to create any additional families specified via the |usefamily| package option.\footnote{The loop here is accomplished via \texttt{etoolbox}.  \texttt{pgffor} might be an alternative, but making definitions global requires trickery.}
@@ -4081,24 +4201,7 @@
     \ifstrequal{#1}{ruby}{\makepythontexfamily[pyglexer=ruby]{ruby}}{}%
     \ifstrequal{#1}{rb}{\makepythontexfamily[pyglexer=ruby]{rb}}{}%
     \ifstrequal{#1}{julia}{\makepythontexfamily[pyglexer=julia]{julia}}{}%
-    \ifstrequal{#1}{juliacon}{\pgfkeys{/PYTX/family, name=juliacon, default, pyglexer=jlcon}%
-                              \expandafter\xdef\csname pytx at macroformatter@juliacon\endcsname{\pytx at tmp@pprinter}%
-                              \expandafter\gdef\csname pytx at fvsettings@juliacon\endcsname{}%
-                              \expandafter\xdef\csname pytx at pyglexer@juliacon\endcsname{\pytx at tmp@pyglexer}%
-                              \expandafter\xdef\csname pytx at pygopt@juliacon\endcsname{\pytx at tmp@pygopt}%
-                              \expandafter\xdef\csname pytx at console@juliacon\endcsname{\pytx at tmp@console}%
-                              \AtEndDocument{\immediate\write\pytx at codefile{pygfamily=juliacon|%
-                                      \csname pytx at pyglexer@juliacon\endcsname|%
-                                      \csname pytx at pygopt@juliacon\endcsname}%
-                                  }%
-                              \pytx at MakeCodeFV{juliacon}%
-                              \newenvironment{juliaconsole}%
-                               {\VerbatimEnvironment
-                                \def\pytx at type{juliacon}%
-                                \pytx at ConfigPygments
-                                \begin{juliaconcode}}%
-                               {\end{juliaconcode}%
-                                \ifbool{pytx at opt@autoprint}{}{\printpythontex{}}}}{}%
+    \ifstrequal{#1}{juliacon}{\makepythontexfamily at con[jlcon]{julia}}{}%
     \ifstrequal{#1}{jl}{\makepythontexfamily[pyglexer=julia]{jl}}{}%
     \ifstrequal{#1}{matlab}{\makepythontexfamily[pyglexer=matlab]{matlab}}{}%
     \ifstrequal{#1}{octave}{\makepythontexfamily[pyglexer=octave]{octave}}{}%
@@ -4106,6 +4209,14 @@
     \ifstrequal{#1}{sage}{\makepythontexfamily[pyglexer=sage]{sage}}{}%
     \ifstrequal{#1}{rust}{\makepythontexfamily[pyglexer=rust]{rust}}{}%
     \ifstrequal{#1}{rs}{\makepythontexfamily[pyglexer=rust]{rs}}{}%
+    \ifstrequal{#1}{R}{\makepythontexfamily[pyglexer=r]{R}}{}%
+    \ifstrequal{#1}{Rcon}{\makepythontexfamily at con[rconsole]{R}}{}%
+    \ifstrequal{#1}{perl}{\makepythontexfamily[pyglexer=perl]{perl}}{}%
+    \ifstrequal{#1}{pl}{\makepythontexfamily[pyglexer=perl]{pl}}{}%
+    \ifstrequal{#1}{perlsix}{\makepythontexfamily[pyglexer=perl6]{perlsix}}{}%
+    \ifstrequal{#1}{psix}{\makepythontexfamily[pyglexer=perl6]{psix}}{}%
+    \ifstrequal{#1}{javascript}{\makepythontexfamily[pyglexer=js]{javascript}}{}%
+    \ifstrequal{#1}{js}{\makepythontexfamily[pyglexer=js]{js}}{}%
 }
 \expandafter\docsvlist\expandafter{\pytx at families}
 %    \end{macrocode}

Modified: trunk/Master/texmf-dist/source/latex/pythontex/pythontex.ins
===================================================================
--- trunk/Master/texmf-dist/source/latex/pythontex/pythontex.ins	2019-09-24 20:07:21 UTC (rev 52173)
+++ trunk/Master/texmf-dist/source/latex/pythontex/pythontex.ins	2019-09-24 20:07:38 UTC (rev 52174)
@@ -1,4 +1,4 @@
-%% Copyright (C) 2012-2016 by Geoffrey M. Poore <gpoore at gmail.com>
+%% Copyright (C) 2012-2019 by Geoffrey M. Poore <gpoore at gmail.com>
 %% --------------------------------------------------------------------------
 %% This work may be distributed and/or modified under the
 %% conditions of the LaTeX Project Public License, either version 1.3
@@ -25,7 +25,7 @@
 
 This is a generated file.
 
-Copyright (C) 2012-2016 by Geoffrey M. Poore <gpoore at gmail.com>
+Copyright (C) 2012-2019 by Geoffrey M. Poore <gpoore at gmail.com>
 --------------------------------------------------------------------------
 This work may be distributed and/or modified under the
 conditions of the LaTeX Project Public License, either version 1.3

Modified: trunk/Master/texmf-dist/tex/latex/pythontex/pythontex.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/pythontex/pythontex.sty	2019-09-24 20:07:21 UTC (rev 52173)
+++ trunk/Master/texmf-dist/tex/latex/pythontex/pythontex.sty	2019-09-24 20:07:38 UTC (rev 52174)
@@ -8,7 +8,7 @@
 %% 
 %% This is a generated file.
 %% 
-%% Copyright (C) 2012-2016 by Geoffrey M. Poore <gpoore at gmail.com>
+%% Copyright (C) 2012-2019 by Geoffrey M. Poore <gpoore at gmail.com>
 %% --------------------------------------------------------------------------
 %% This work may be distributed and/or modified under the
 %% conditions of the LaTeX Project Public License, either version 1.3
@@ -20,11 +20,10 @@
 %% 
 \NeedsTeXFormat{LaTeX2e}[1999/12/01]
 \ProvidesPackage{pythontex}
-    [2017/07/20 v0.16 execute and typeset Python code and other languages]
+    [2019/09/22 v0.17 execute and typeset Python code and other languages]
 
-
 \newcommand{\pytx at packagename}{PythonTeX}
-\newcommand{\pytx at packageversion}{0.16}
+\newcommand{\pytx at packageversion}{0.17}
 \RequirePackage{fvextra}
 \RequirePackage{etoolbox}
 \RequirePackage{xstring}
@@ -847,7 +846,7 @@
     }%
     \xdef\pytx at type{CC:#2:#1}%
     \edef\pytx at cmd{c}%
-    \def\pytx at context{}%
+    \pytx at SetContext
     \def\pytx at group{none}%
     \let\pytx at InlineShow\@empty
     \let\pytx at InlineSave\pytx at InlineSaveCode
@@ -1038,6 +1037,22 @@
     }%
     {\end{VerbatimOut}%
     \xdef\pytx at stdfile{\pytx at type_\pytx at session_\pytx at group_\arabic{\pytx at counter}}%
+    \ifcsname pytx at nonpyconsole@\pytx at type\endcsname
+        \ifcsname pytx at code@as at console\endcsname
+            \setcounter{FancyVerbLine}{\value{\pytx at linecount}}%
+            \pytx at FVSet
+            \ifdefstring{\pytx at fvopttmp}{}{}{\expandafter\fvset\expandafter{\pytx at fvopttmp}}%
+            \pytx at ConfigPygments
+            \InputIfFileExists{\pytx at outputdir/\pytx at stdfile.stdout}%
+               {\DepyFile{p:\pytx at outputdir/\pytx at stdfile.stdout}}%
+               {\par\textbf{??~\pytx at packagename~??}\par
+                \PackageWarning{\pytx at packagename}{Non-existent console content}}%
+            \setcounter{\pytx at linecount}{\value{FancyVerbLine}}%
+        \else
+        \fi
+        \let\pytx at EnvAutoprint\relax
+    \else
+    \fi
     \setcounter{FancyVerbLine}{\value{pytx at FancyVerbLineTemp}}%
     \stepcounter{\pytx at counter}%
     \pytx at EnvAutoprint
@@ -1047,6 +1062,8 @@
     \ifstrempty{#1}{\edef\pytx at session{default}}{\StrSubstitute{#1}{:}{-}[\pytx at session]}%
     \xdef\pytx at counter{pytx@\pytx at type @\pytx at session @\pytx at group}%
     \pytx at CheckCounter{\pytx at counter}%
+    \edef\pytx at linecount{\pytx at counter @line}%
+    \pytx at CheckCounter{\pytx at linecount}%
     \pytx at WriteCodefileInfo
     \begingroup
     \obeylines
@@ -1053,6 +1070,8 @@
     \@ifnextchar[{\endgroup\pytx at BeginCodeEnv@i}{\endgroup\pytx at BeginCodeEnv@i[]}%
 }%
 \def\pytx at BeginCodeEnv@i[#1]{%
+    \def\pytx at fvopttmp{#1}%
+    \def\pytx at argspprint{#1}%
     \setcounter{pytx at FancyVerbLineTemp}{\value{FancyVerbLine}}%
     \let\FVB at VerbatimOut\pytx at FVB@VerbatimOut
     \let\FVE at VerbatimOut\pytx at FVE@VerbatimOut
@@ -1069,7 +1088,7 @@
     }%
     \xdef\pytx at type{CC:#2:#1}%
     \edef\pytx at cmd{code}%
-    \def\pytx at context{}%
+    \pytx at SetContext
     \def\pytx at group{none}%
     \pytx at BeginCodeEnv[none]}%
 {\end{VerbatimOut}%
@@ -1079,7 +1098,7 @@
 \newcommand{\pytx at MakeSubFV}[1]{%
     \expandafter\newenvironment{#1sub}{%
         \VerbatimEnvironment
-        \Depythontex{env:#1sub:om|:p}%
+        \Depythontex{env:#1sub:oo|:p}%
         \xdef\pytx at type{#1}%
         \edef\pytx at cmd{sub}%
         \pytx at SetContext
@@ -1266,6 +1285,27 @@
     \newcounter{pytx@#2 at default@default}%
 }
 \@onlypreamble\makepythontexfamily
+\newcommand{\makepythontexfamily at con}[2][text]{%
+    \pgfkeys{/PYTX/family, name=#2con, default, pyglexer=#1, console=true}%
+    \expandafter\xdef\csname pytx at macroformatter@#2con\endcsname{\pytx at tmp@pprinter}%
+    \expandafter\gdef\csname pytx at fvsettings@#2con\endcsname{}%
+    \expandafter\xdef\csname pytx at pyglexer@#2con\endcsname{\pytx at tmp@pyglexer}%
+    \expandafter\xdef\csname pytx at pygopt@#2con\endcsname{\pytx at tmp@pygopt}%
+    \expandafter\xdef\csname pytx at console@#2con\endcsname{\pytx at tmp@console}%
+    \AtEndDocument{\immediate\write\pytx at codefile{pygfamily=#2con|%
+        \csname pytx at pyglexer@#2con\endcsname|%
+        \csname pytx at pygopt@#2con\endcsname}%
+    }%
+    \pytx at MakeCodeFV{#2con}%
+    \expandafter\global\expandafter\let\csname pytx at nonpyconsole@#2con\endcsname\relax
+    \newenvironment{#2console}%
+       {\VerbatimEnvironment
+        \def\pytx at type{#2con}%
+        \let\pytx at code@as at console\relax
+        \begin{#2concode}}%
+       {\end{#2concode}}%
+}
+
 \newcommand{\setpythontexpyglexer}[2][]{%
     \Depythontex{cmd:setpythontexpyglexer:om:n}%
     \ifstrempty{#1}{\def\pytx at pyglexer{#2}}{%
@@ -1308,34 +1348,17 @@
     }%
 }
 \@onlypreamble\setpythontexprettyprinter
-\makepythontexfamily[pyglexer=python]{py}
+\makepythontexfamily[pyglexer=python3]{py}
 \makepythontexfamily[pyglexer=pycon, console=true]{pycon}
-\makepythontexfamily[pyglexer=python]{sympy}
+\makepythontexfamily[pyglexer=python3]{sympy}
 \makepythontexfamily[pyglexer=pycon, console=true]{sympycon}
-\makepythontexfamily[pyglexer=python]{pylab}
+\makepythontexfamily[pyglexer=python3]{pylab}
 \makepythontexfamily[pyglexer=pycon, console=true]{pylabcon}
 \renewcommand{\do}[1]{%
     \ifstrequal{#1}{ruby}{\makepythontexfamily[pyglexer=ruby]{ruby}}{}%
     \ifstrequal{#1}{rb}{\makepythontexfamily[pyglexer=ruby]{rb}}{}%
     \ifstrequal{#1}{julia}{\makepythontexfamily[pyglexer=julia]{julia}}{}%
-    \ifstrequal{#1}{juliacon}{\pgfkeys{/PYTX/family, name=juliacon, default, pyglexer=jlcon}%
-                              \expandafter\xdef\csname pytx at macroformatter@juliacon\endcsname{\pytx at tmp@pprinter}%
-                              \expandafter\gdef\csname pytx at fvsettings@juliacon\endcsname{}%
-                              \expandafter\xdef\csname pytx at pyglexer@juliacon\endcsname{\pytx at tmp@pyglexer}%
-                              \expandafter\xdef\csname pytx at pygopt@juliacon\endcsname{\pytx at tmp@pygopt}%
-                              \expandafter\xdef\csname pytx at console@juliacon\endcsname{\pytx at tmp@console}%
-                              \AtEndDocument{\immediate\write\pytx at codefile{pygfamily=juliacon|%
-                                      \csname pytx at pyglexer@juliacon\endcsname|%
-                                      \csname pytx at pygopt@juliacon\endcsname}%
-                                  }%
-                              \pytx at MakeCodeFV{juliacon}%
-                              \newenvironment{juliaconsole}%
-                               {\VerbatimEnvironment
-                                \def\pytx at type{juliacon}%
-                                \pytx at ConfigPygments
-                                \begin{juliaconcode}}%
-                               {\end{juliaconcode}%
-                                \ifbool{pytx at opt@autoprint}{}{\printpythontex{}}}}{}%
+    \ifstrequal{#1}{juliacon}{\makepythontexfamily at con[jlcon]{julia}}{}%
     \ifstrequal{#1}{jl}{\makepythontexfamily[pyglexer=julia]{jl}}{}%
     \ifstrequal{#1}{matlab}{\makepythontexfamily[pyglexer=matlab]{matlab}}{}%
     \ifstrequal{#1}{octave}{\makepythontexfamily[pyglexer=octave]{octave}}{}%
@@ -1343,6 +1366,14 @@
     \ifstrequal{#1}{sage}{\makepythontexfamily[pyglexer=sage]{sage}}{}%
     \ifstrequal{#1}{rust}{\makepythontexfamily[pyglexer=rust]{rust}}{}%
     \ifstrequal{#1}{rs}{\makepythontexfamily[pyglexer=rust]{rs}}{}%
+    \ifstrequal{#1}{R}{\makepythontexfamily[pyglexer=r]{R}}{}%
+    \ifstrequal{#1}{Rcon}{\makepythontexfamily at con[rconsole]{R}}{}%
+    \ifstrequal{#1}{perl}{\makepythontexfamily[pyglexer=perl]{perl}}{}%
+    \ifstrequal{#1}{pl}{\makepythontexfamily[pyglexer=perl]{pl}}{}%
+    \ifstrequal{#1}{perlsix}{\makepythontexfamily[pyglexer=perl6]{perlsix}}{}%
+    \ifstrequal{#1}{psix}{\makepythontexfamily[pyglexer=perl6]{psix}}{}%
+    \ifstrequal{#1}{javascript}{\makepythontexfamily[pyglexer=js]{javascript}}{}%
+    \ifstrequal{#1}{js}{\makepythontexfamily[pyglexer=js]{js}}{}%
 }
 \expandafter\docsvlist\expandafter{\pytx at families}
 \newbool{pytx at listingenv}



More information about the tex-live-commits mailing list