def compile_file(self, filepath, write=True, package=False, force=False, **kwargs): """Compile a file and returns the compiled file's path.""" set_ext = False if write is False: destpath = None elif write is True: destpath = filepath set_ext = True elif os.path.splitext(write)[1]: # write is a file; it is the destination filepath destpath = write else: # write is a dir; make the destination filepath by adding the filename destpath = os.path.join(write, os.path.basename(filepath)) set_ext = True if set_ext: base, ext = os.path.splitext(os.path.splitext(destpath)[0]) if not ext: ext = comp_ext destpath = fixpath(base + ext) if filepath == destpath: raise CoconutException("cannot compile " + showpath(filepath) + " to itself", extra="incorrect file extension") if destpath is not None: dest_ext = os.path.splitext(destpath)[1] if dest_ext in code_exts: if force: logger.warn("found destination path with " + dest_ext + " extension; compiling anyway due to --force") else: raise CoconutException( "found destination path with " + dest_ext + " extension; aborting compilation", extra="pass --force to override", ) self.compile(filepath, destpath, package, force=force, **kwargs) return destpath
def set_style(self, style): """Set pygments syntax highlighting style.""" if style == "none": self.style = None elif prompt_toolkit is None: raise CoconutException("syntax highlighting is not supported on this Python version") elif style == "list": print("Coconut Styles: none, " + ", ".join(pygments.styles.get_all_styles())) sys.exit(0) elif style in pygments.styles.get_all_styles(): self.style = style else: raise CoconutException("unrecognized pygments style", style, extra="use '--style list' to show all valid styles")
def set_recursion_limit(self, limit): """Sets the Python recursion limit.""" if limit < minimum_recursion_limit: raise CoconutException("--recursion-limit must be at least " + str(minimum_recursion_limit)) else: sys.setrecursionlimit(limit)
def compile_file(self, filepath, write=True, package=False, *args, **kwargs): """Compile a file and returns the compiled file's path.""" set_ext = False if write is False: destpath = None elif write is True: destpath = filepath set_ext = True elif os.path.splitext(write)[1]: # write is a file; it is the destination filepath destpath = write else: # write is a dir; make the destination filepath by adding the filename destpath = os.path.join(write, os.path.basename(filepath)) set_ext = True if set_ext: base, ext = os.path.splitext(os.path.splitext(destpath)[0]) if not ext: ext = comp_ext destpath = fixpath(base + ext) if filepath == destpath: raise CoconutException("cannot compile " + showpath(filepath) + " to itself", extra="incorrect file extension") self.compile(filepath, destpath, package, *args, **kwargs) return destpath
def compile_file(self, filepath, write=True, package=False, *args, **kwargs): """Compile a file and returns the compiled file's path.""" if write is None: destpath = None elif write is True: destpath = filepath else: # add the name of file to the destination path destpath = os.path.join(write, os.path.basename(filepath)) if destpath is not None: base, ext = os.path.splitext(os.path.splitext(destpath)[0]) if not ext: ext = comp_ext destpath = fixpath(base + ext) if filepath == destpath: raise CoconutException("cannot compile " + showpath(filepath) + " to itself", extra="incorrect file extension") self.compile(filepath, destpath, package, *args, **kwargs) return destpath
def version(which="num"): """Gets the Coconut version.""" if which in VERSIONS: return VERSIONS[which] else: raise CoconutException("invalid version type " + ascii(which), extra="valid versions are " + ", ".join(VERSIONS))
def parse(code, mode="sys"): """Parses Coconut code.""" if CLI.comp is None: setup() if mode in PARSERS: return PARSERS[mode](CLI.comp)(code) else: raise CoconutException("invalid parse mode " + ascii(mode), extra="valid modes are " + ", ".join(PARSERS))
def parse(code="", mode="sys"): """Compile Coconut code.""" if CLI.comp is None: setup() if mode not in PARSERS: raise CoconutException( "invalid parse mode " + repr(mode), extra="valid modes are " + ", ".join(PARSERS), ) return PARSERS[mode](CLI.comp)(code)
def site_uninstall(self): """Remove Coconut's pth file from site-packages.""" python_lib = self.get_python_lib() pth_file = os.path.join(python_lib, os.path.basename(coconut_pth_file)) if os.path.isfile(pth_file): os.remove(pth_file) logger.show_sig("Removed %s from %s." % (os.path.basename(coconut_pth_file), python_lib)) else: raise CoconutException("failed to find %s file to remove" % (os.path.basename(coconut_pth_file),))
def start_jupyter(self, args): """Starts Jupyter with the Coconut kernel.""" def install_func(cmd): """Runs an installation command.""" logger.log_cmd(cmd) if args and not logger.verbose: subprocess.check_output(cmd, stderr=subprocess.STDOUT) else: subprocess.check_call(cmd) try: install_func(["jupyter", "--version"]) except subprocess.CalledProcessError: jupyter = "ipython" else: jupyter = "jupyter" for icoconut_kernel_dir in icoconut_kernel_dirs: install_args = [ jupyter, "kernelspec", "install", icoconut_kernel_dir, "--replace" ] try: install_func(install_args) except subprocess.CalledProcessError: user_install_args = install_args + ["--user"] try: install_func(user_install_args) except subprocess.CalledProcessError: args = "kernel install failed on command'", " ".join( install_args) self.comp.warn(*args) self.register_error(errmsg="Jupyter error") if args: if args[0] == "console": ver = "2" if PY2 else "3" try: install_func( ["python" + ver, "-m", "coconut.main", "--version"]) except subprocess.CalledProcessError: kernel_name = "coconut" else: kernel_name = "coconut" + ver run_args = [jupyter, "console", "--kernel", kernel_name ] + args[1:] elif args[0] == "notebook": run_args = [jupyter, "notebook"] + args[1:] else: raise CoconutException( 'first argument after --jupyter must be either "console" or "notebook"' ) logger.log_cmd(run_args) self.register_error(subprocess.call(run_args), errmsg="Jupyter error")
def compile_path(self, path, write=True, package=True, *args, **kwargs): """Compile a path and returns paths to compiled files.""" if not isinstance(write, bool): write = fixpath(write) if os.path.isfile(path): destpath = self.compile_file(path, write, package, *args, **kwargs) return [destpath] if destpath is not None else [] elif os.path.isdir(path): return self.compile_folder(path, write, package, *args, **kwargs) else: raise CoconutException("could not find source path", path)
def set_jobs(self, jobs): """Set --jobs.""" if jobs == "sys": self.jobs = None else: try: jobs = int(jobs) except ValueError: jobs = -1 # will raise error below if jobs < 0: raise CoconutException("--jobs must be an integer >= 0 or 'sys'") self.jobs = jobs
def process_source_dest(self, source, dest, args): """Determine the correct source, dest, package mode to use for the given source, dest, and args.""" # determine source processed_source = fixpath(source) # validate args if (args.run or args.interact) and os.path.isdir(processed_source): if args.run: raise CoconutException("source path %r must point to file not directory when --run is enabled" % (source,)) if args.interact: raise CoconutException("source path %r must point to file not directory when --run (implied by --interact) is enabled" % (source,)) if args.watch and os.path.isfile(processed_source): raise CoconutException("source path %r must point to directory not file when --watch is enabled" % (source,)) # determine dest if dest is None: if args.no_write: processed_dest = False # no dest else: processed_dest = True # auto-generate dest elif args.no_write: raise CoconutException("destination path cannot be given when --no-write is enabled") else: processed_dest = dest # determine package mode if args.package or self.mypy: package = True elif args.standalone: package = False else: # auto-decide package if os.path.isfile(source): package = False elif os.path.isdir(source): package = True else: raise CoconutException("could not find source path", source) return processed_source, processed_dest, package
def cmd(self, args=None, argv=None, interact=True): """Process command-line arguments.""" if args is None: parsed_args = arguments.parse_args() else: parsed_args = arguments.parse_args(args) if argv is not None: if parsed_args.argv is not None: raise CoconutException("cannot pass --argv/--args when using coconut-run (coconut-run interprets any arguments after the source file as --argv/--args)") parsed_args.argv = argv self.exit_code = 0 with self.handling_exceptions(): self.use_args(parsed_args, interact, original_args=args) self.exit_on_error()
def start_jupyter(self, args): """Starts Jupyter with the Coconut kernel.""" install_func = functools.partial(run_cmd, show_output=logger.verbose or not args) try: install_func(["jupyter", "--version"]) except CalledProcessError: jupyter = "ipython" else: jupyter = "jupyter" for icoconut_kernel_dir in icoconut_kernel_dirs: install_args = [ jupyter, "kernelspec", "install", icoconut_kernel_dir, "--replace" ] try: install_func(install_args) except CalledProcessError: user_install_args = install_args + ["--user"] try: install_func(user_install_args) except CalledProcessError: logger.warn("kernel install failed on command'", " ".join(install_args)) self.register_error(errmsg="Jupyter error") if args: if args[0] == "console": ver = "2" if PY2 else "3" try: install_func( ["python" + ver, "-m", "coconut.main", "--version"]) except CalledProcessError: kernel_name = "coconut" else: kernel_name = "coconut" + ver run_args = [jupyter, "console", "--kernel", kernel_name ] + args[1:] elif args[0] == "notebook": run_args = [jupyter, "notebook"] + args[1:] else: raise CoconutException( "first argument after --jupyter must be either 'console' or 'notebook'" ) self.register_error(run_cmd(run_args, raise_errs=False), errmsg="Jupyter error")
def version(which="num"): """Gets the Coconut version.""" if which == "num": return VERSION elif which == "name": return VERSION_NAME elif which == "spec": return VERSION_STR elif which == "tag": return version_tag elif which == "-v": return main_sig + version_long else: raise CoconutException("invalid version type " + ascii(which) + "; valid versions are 'num', 'name', 'spec', 'tag', and '-v'")
def get_coconut_encoding(encoding="coconut"): """Get a CodecInfo for the given Coconut encoding.""" if not encoding.startswith("coconut"): return None if encoding != "coconut": raise CoconutException("unknown Coconut encoding: " + repr(encoding)) return codecs.CodecInfo( name=encoding, encode=encodings.utf_8.encode, decode=CoconutStreamReader.decode, incrementalencoder=encodings.utf_8.IncrementalEncoder, incrementaldecoder=CoconutIncrementalDecoder, streamreader=CoconutStreamReader, streamwriter=encodings.utf_8.StreamWriter, )
def compile_path(self, path, write=True, package=None, *args, **kwargs): """Compile a path and returns paths to compiled files.""" path = fixpath(path) if write is not None and write is not True: write = fixpath(write) if os.path.isfile(path): if package is None: package = False destpath = self.compile_file(path, write, package, *args, **kwargs) return [destpath] if destpath is not None else [] elif os.path.isdir(path): if package is None: package = True return self.compile_folder(path, write, package, *args, **kwargs) else: raise CoconutException("could not find source path", path)
def compile_path(self, path, write=True, package=None, run=False, force=False): """Compiles a path.""" path = fixpath(path) if write is not None and write is not True: write = fixpath(write) if os.path.isfile(path): if package is None: package = False self.compile_file(path, write, package, run, force) elif os.path.isdir(path): if package is None: package = True self.compile_folder(path, write, package, run, force) else: raise CoconutException("could not find source path", path)
def parse(code, mode="exec"): """Parses Coconut code.""" if CLI.comp is None: setup() if mode == "single": return CLI.comp.parse_single(code) elif mode == "file": return CLI.comp.parse_file(code) elif mode == "exec": return CLI.comp.parse_exec(code) elif mode == "module": return CLI.comp.parse_module(code) elif mode == "block": return CLI.comp.parse_block(code) elif mode == "eval": return CLI.comp.parse_eval(code) elif mode == "debug": return CLI.comp.parse_debug(code) else: raise CoconutException("invalid parse mode " + ascii(mode) + "; valid modes are 'exec', 'file', 'single', 'module', 'block', 'eval', and 'debug'")
def compile_folder(self, directory, write=True, package=True, *args, **kwargs): """Compile a directory and returns paths to compiled files.""" if not isinstance(write, bool) and os.path.isfile(write): raise CoconutException("destination path cannot point to a file when compiling a directory") filepaths = [] for dirpath, dirnames, filenames in os.walk(directory): if isinstance(write, bool): writedir = write else: writedir = os.path.join(write, os.path.relpath(dirpath, directory)) for filename in filenames: if os.path.splitext(filename)[1] in code_exts: with self.handling_exceptions(): destpath = self.compile_file(os.path.join(dirpath, filename), writedir, package, *args, **kwargs) if destpath is not None: filepaths.append(destpath) for name in dirnames[:]: if not is_special_dir(name) and name.startswith("."): if logger.verbose: logger.show_tabulated("Skipped directory", name, "(explicitly pass as source to override).") dirnames.remove(name) # directories removed from dirnames won't appear in further os.walk iterations return filepaths
def compile_file(self, filepath, write=True, package=False, run=False, force=False): """Compiles a file.""" if write is None: destpath = None elif write is True: destpath = filepath else: destpath = os.path.join(write, os.path.basename(filepath)) if destpath is not None: base, ext = os.path.splitext(os.path.splitext(destpath)[0]) if not ext: ext = comp_ext destpath = base + ext if filepath == destpath: raise CoconutException("cannot compile " + showpath(filepath) + " to itself (incorrect file extension)") else: self.compile(filepath, destpath, package, run, force)
def set_mypy_args(self, mypy_args=None): """Set MyPy arguments.""" if mypy_args is None: self.mypy_args = None elif mypy_install_arg in mypy_args: if mypy_args != [mypy_install_arg]: raise CoconutException("'--mypy install' cannot be used alongside other --mypy arguments") stub_dir = set_mypy_path() logger.show_sig("Successfully installed MyPy stubs into " + repr(stub_dir)) self.mypy_args = None else: self.mypy_args = list(mypy_args) if not any(arg.startswith("--python-version") for arg in self.mypy_args): self.mypy_args += [ "--python-version", ver_tuple_to_str(get_target_info_smart(self.comp.target, mode="mypy")), ] if not any(arg.startswith("--python-executable") for arg in self.mypy_args): self.mypy_args += [ "--python-executable", sys.executable, ] add_mypy_args = default_mypy_args + (verbose_mypy_args if logger.verbose else ()) for arg in add_mypy_args: no_arg = invert_mypy_arg(arg) arg_prefixes = (arg,) + ((no_arg,) if no_arg is not None else ()) if not any(arg.startswith(arg_prefixes) for arg in self.mypy_args): self.mypy_args.append(arg) logger.log("MyPy args:", self.mypy_args) self.mypy_errs = []
try: from IPython.core.inputsplitter import IPythonInputSplitter from IPython.core.interactiveshell import InteractiveShellABC from IPython.core.compilerop import CachingCompiler from ipykernel.ipkernel import IPythonKernel from ipykernel.zmqshell import ZMQInteractiveShell except ImportError: LOAD_MODULE = False if os.environ.get(conda_build_env_var): # conda tries to import coconut.icoconut as a test even when IPython isn't available logger.warn("Missing IPython but detected " + conda_build_env_var + "; skipping coconut.icoconut loading") else: raise CoconutException( "--jupyter flag requires Jupyter library", extra="run 'pip install coconut[jupyter]' to fix", ) else: LOAD_MODULE = True # ----------------------------------------------------------------------------------------------------------------------- # GLOBALS: # ----------------------------------------------------------------------------------------------------------------------- COMPILER = Compiler( target="sys", line_numbers=True, keep_lines=True, ) RUNNER = Runner(COMPILER)
# IMPORTS: # ----------------------------------------------------------------------------------------------------------------------- from __future__ import print_function, absolute_import, unicode_literals, division from coconut.root import * # NOQA from coconut.exceptions import CoconutException try: from watchdog.events import FileSystemEventHandler # import Observer to provide it for others to import from watchdog.observers import Observer # NOQA except ImportError: raise CoconutException( "--watch flag requires watchdog library", extra="run 'pip install coconut[watch]' to fix", ) # ----------------------------------------------------------------------------------------------------------------------- # CLASSES: # ----------------------------------------------------------------------------------------------------------------------- class RecompilationWatcher(FileSystemEventHandler): """Watcher that recompiles modified files.""" def __init__(self, recompile): super(RecompilationWatcher, self).__init__() self.recompile = recompile self.keep_watching() def keep_watching(self):
def start_jupyter(self, args): """Start Jupyter with the Coconut kernel.""" install_func = functools.partial(run_cmd, show_output=logger.verbose) try: install_func(["jupyter", "--version"]) except CalledProcessError: jupyter = "ipython" else: jupyter = "jupyter" # always install kernels if given no args, otherwise only if there's a kernel missing do_install = not args if not do_install: kernel_list = run_cmd([jupyter, "kernelspec", "list"], show_output=False, raise_errs=False) do_install = any(ker not in kernel_list for ker in icoconut_kernel_names) if do_install: success = True for icoconut_kernel_dir in icoconut_kernel_dirs: install_args = [ jupyter, "kernelspec", "install", icoconut_kernel_dir, "--replace" ] try: install_func(install_args) except CalledProcessError: user_install_args = install_args + ["--user"] try: install_func(user_install_args) except CalledProcessError: logger.warn("kernel install failed on command'", " ".join(install_args)) self.register_error(errmsg="Jupyter error") success = False if success: logger.show_sig( "Successfully installed Coconut Jupyter kernel.") if args: if args[0] == "console": ver = "2" if PY2 else "3" try: install_func( ["python" + ver, "-m", "coconut.main", "--version"]) except CalledProcessError: kernel_name = "coconut" else: kernel_name = "coconut" + ver run_args = [jupyter, "console", "--kernel", kernel_name ] + args[1:] elif args[0] == "notebook": run_args = [jupyter, "notebook"] + args[1:] else: raise CoconutException( "first argument after --jupyter must be either 'console' or 'notebook'" ) self.register_error(run_cmd(run_args, raise_errs=False), errmsg="Jupyter error")
def use_args(self, args, interact=True, original_args=None): """Handle command-line arguments.""" logger.quiet, logger.verbose = args.quiet, args.verbose if DEVELOP: logger.tracing = args.trace logger.log("Using " + PYPARSING + ".") if original_args is not None: logger.log("Directly passed args:", original_args) logger.log("Parsed args:", args) if args.recursion_limit is not None: set_recursion_limit(args.recursion_limit) if args.jobs is not None: self.set_jobs(args.jobs) if args.display: self.show = True if args.style is not None: self.prompt.set_style(args.style) if args.documentation: launch_documentation() if args.tutorial: launch_tutorial() self.setup( target=args.target, strict=args.strict, minify=args.minify, line_numbers=args.line_numbers, keep_lines=args.keep_lines, no_tco=args.no_tco or args.mypy is not None, ) if args.mypy is not None: if args.no_tco: logger.warn( "extraneous --no-tco argument passed; --mypy implies --no-tco" ) self.set_mypy_args(args.mypy) if args.argv is not None: sys.argv = [args.source if args.source is not None else ""] sys.argv.extend(args.argv) if args.source is not None: if args.interact and args.run: logger.warn( "extraneous --run argument passed; --interact implies --run" ) if args.package and self.mypy: logger.warn( "extraneous --package argument passed; --mypy implies --package" ) if args.standalone and args.package: raise CoconutException( "cannot compile as both --package and --standalone") if args.standalone and self.mypy: raise CoconutException( "cannot compile as both --package (implied by --mypy) and --standalone" ) if args.no_write and self.mypy: raise CoconutException( "cannot compile with --no-write when using --mypy") if (args.run or args.interact) and os.path.isdir(args.source): if args.run: raise CoconutException( "source path must point to file not directory when --run is enabled" ) if args.interact: raise CoconutException( "source path must point to file not directory when --run (implied by --interact) is enabled" ) if args.watch and os.path.isfile(args.source): raise CoconutException( "source path must point to directory not file when --watch is enabled" ) if args.dest is None: if args.no_write: dest = None # no dest else: dest = True # auto-generate dest elif args.no_write: raise CoconutException( "destination path cannot be given when --no-write is enabled" ) elif os.path.isfile(args.dest): raise CoconutException( "destination path must point to directory not file") else: dest = args.dest if args.package or self.mypy: package = True elif args.standalone: package = False else: package = None # auto-decide package with self.running_jobs(exit_on_error=not args.watch): filepaths = self.compile_path(args.source, dest, package, args.run or args.interact, args.force) self.run_mypy(filepaths) elif (args.run or args.no_write or args.force or args.package or args.standalone or args.watch): raise CoconutException( "a source file/folder must be specified when options that depend on the source are enabled" ) if args.code is not None: self.execute(self.comp.parse_block(args.code)) got_stdin = False if stdin_readable(): logger.log("Reading piped input from stdin...") self.execute(self.comp.parse_block(sys.stdin.read())) got_stdin = True if args.jupyter is not None: self.start_jupyter(args.jupyter) if args.interact or (interact and not (got_stdin or args.source or args.code or args.tutorial or args.documentation or args.watch or args.jupyter is not None)): self.start_prompt() if args.watch: self.watch(args.source, dest, package, args.run, args.force)
# IMPORTS: #----------------------------------------------------------------------------------------------------------------------- from __future__ import print_function, absolute_import, unicode_literals, division from coconut.root import * # NOQA import traceback from coconut.exceptions import CoconutException try: from mypy.api import run except ImportError: raise CoconutException( "--mypy flag requires MyPy library", extra="run 'pip install coconut[mypy]' to fix", ) #----------------------------------------------------------------------------------------------------------------------- # CLASSES: #----------------------------------------------------------------------------------------------------------------------- def mypy_run(args): """Runs mypy with given arguments and shows the result.""" try: stdout, stderr, exit_code = run(args) except BaseException: traceback.print_exc() else: for line in stdout.splitlines():
def use_args(self, args, interact=True): """Handles command-line arguments.""" logger.quiet, logger.verbose = args.quiet, args.verbose if args.recursion_limit is not None: self.set_recursion_limit(args.recursion_limit) if args.jobs is not None: self.set_jobs(args.jobs) if args.tutorial: self.launch_tutorial() if args.documentation: self.launch_documentation() if args.style is not None: self.prompt.set_style(args.style) if args.display: self.show = True self.setup( target=args.target, strict=args.strict, minify=args.minify, line_numbers=args.line_numbers, keep_lines=args.keep_lines, ) if args.source is not None: if args.run and os.path.isdir(args.source): raise CoconutException( "source path must point to file not directory when --run is enabled" ) elif args.watch and os.path.isfile(args.source): raise CoconutException( "source path must point to directory not file when --watch is enabled" ) if args.dest is None: if args.nowrite: dest = None # no dest else: dest = True # auto-generate dest elif args.nowrite: raise CoconutException( "destination path cannot be given when --nowrite is enabled" ) elif os.path.isfile(args.dest): raise CoconutException( "destination path must point to directory not file") else: dest = args.dest if args.package and args.standalone: raise CoconutException( "cannot compile as both --package and --standalone") elif args.package: package = True elif args.standalone: package = False else: package = None # auto-decide package with self.running_jobs(): self.compile_path(args.source, dest, package, args.run, args.force) elif (args.run or args.nowrite or args.force or args.package or args.standalone or args.watch): raise CoconutException( "a source file/folder must be specified when options that depend on the source are enabled" ) if args.code is not None: self.execute(self.comp.parse_block(args.code)) stdin = not sys.stdin.isatty() # check if input was piped in if stdin: self.execute(self.comp.parse_block(sys.stdin.read())) if args.jupyter is not None: self.start_jupyter(args.jupyter) if args.interact or (interact and not (stdin or args.source or args.code or args.tutorial or args.documentation or args.watch or args.jupyter is not None)): self.start_prompt() if args.watch: self.watch(args.source, dest, package, args.run, args.force)
try: from IPython.core.inputsplitter import IPythonInputSplitter from IPython.core.interactiveshell import InteractiveShellABC from IPython.core.compilerop import CachingCompiler from IPython.terminal.embed import InteractiveShellEmbed from ipykernel.ipkernel import IPythonKernel from ipykernel.zmqshell import ZMQInteractiveShell from ipykernel.kernelapp import IPKernelApp except ImportError: LOAD_MODULE = False if os.environ.get(conda_build_env_var): # conda tries to import coconut.icoconut as a test even when IPython isn't available logger.warn("Missing IPython but detected " + conda_build_env_var + "; skipping coconut.icoconut loading") else: raise CoconutException( "--jupyter flag requires Jupyter library", extra="run '{python} -m pip install coconut[jupyter]' to fix".format(python=sys.executable), ) else: LOAD_MODULE = True # ----------------------------------------------------------------------------------------------------------------------- # UTILITIES: # ----------------------------------------------------------------------------------------------------------------------- COMPILER = Compiler( target="sys", line_numbers=True, keep_lines=True, )