diff --git a/MANIFEST.in b/MANIFEST.in index 2994de541..e21a6d992 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,5 @@ include README.rst -include CHANGES.rst +include CHANGES.md include ChangeLog include COPYING.txt include FUTURE.rst diff --git a/README.rst b/README.rst index 0428d09fa..f4daf998c 100644 --- a/README.rst +++ b/README.rst @@ -70,5 +70,5 @@ Mathics3 is released under the GNU General Public License Version 3 (GPL3). :target: https://badge.fury.io/py/Mathics3 .. |PyPI Installs| image:: https://pepy.tech/badge/Mathics3 .. |Supported Python Versions| image:: https://img.shields.io/pypi/pyversions/Mathics3.svg -.. |Packaging status| image:: https://repology.org/badge/vertical-allrepos/mathics.svg - :target: https://repology.org/project/mathics/versions +.. |Packaging status| image:: https://repology.org/badge/vertical-allrepos/mathics3.svg + :target: https://repology.org/project/mathics3/versions diff --git a/mathics/autoload/formats/Asy/Export.m b/mathics/SystemFiles/Formats/Asy/Export.m similarity index 100% rename from mathics/autoload/formats/Asy/Export.m rename to mathics/SystemFiles/Formats/Asy/Export.m diff --git a/mathics/autoload/formats/Base64/Export.m b/mathics/SystemFiles/Formats/Base64/Export.wl similarity index 94% rename from mathics/autoload/formats/Base64/Export.m rename to mathics/SystemFiles/Formats/Base64/Export.wl index badf6cfad..733c1d057 100644 --- a/mathics/autoload/formats/Base64/Export.m +++ b/mathics/SystemFiles/Formats/Base64/Export.wl @@ -1,3 +1,4 @@ +(* ::Package:: *) Begin["System`Convert`B64Dump`"] @@ -21,6 +22,7 @@ FunctionChannels -> {"FileNames"}, Options -> {"CharacterEncoding", "ByteOrderMark"}, DefaultElement -> "Plaintext", + Encoding -> True BinaryFormat -> False ] diff --git a/mathics/autoload/formats/Base64/Import.m b/mathics/SystemFiles/Formats/Base64/Import.wl similarity index 94% rename from mathics/autoload/formats/Base64/Import.m rename to mathics/SystemFiles/Formats/Base64/Import.wl index c99f2286c..b1cb12a1d 100644 --- a/mathics/autoload/formats/Base64/Import.m +++ b/mathics/SystemFiles/Formats/Base64/Import.wl @@ -1,3 +1,5 @@ +(* ::Package:: *) + Begin["System`Convert`B64Dump`"] @@ -21,6 +23,7 @@ FunctionChannels -> {"FileNames"}, AvailableElements -> {"Data"}, DefaultElement -> "Data", + Encoding -> True Options -> { "CharacterEncoding" } diff --git a/mathics/autoload/formats/CSV/Export.m b/mathics/SystemFiles/Formats/CSV/Export.wl similarity index 94% rename from mathics/autoload/formats/CSV/Export.m rename to mathics/SystemFiles/Formats/CSV/Export.wl index 6cca1dac9..ed1acde61 100644 --- a/mathics/autoload/formats/CSV/Export.m +++ b/mathics/SystemFiles/Formats/CSV/Export.wl @@ -1,3 +1,5 @@ +(* ::Package:: *) + (* CSV Exporter *) Begin["System`Convert`TableDump`"] @@ -8,7 +10,7 @@ }; CSVExport[strm_OutputStream, data_, OptionsPattern[]]:= - Module[{char, wraplist, sep = OptionValue["FieldSeparators"]}, + Module[{char, wraplist, sep = OptionValue["FieldSeparators"]}, If[strm === $Failed, Return[$Failed]]; wraplist[x_] := If[Head[x] === List, x, {x}]; char = Map[ToString, wraplist /@ wraplist[data], {2}]; @@ -21,7 +23,7 @@ System`Convert`TableDump`CSVExport, FunctionChannels -> {"Streams"}, Options -> {"ByteOrderMark"}, - DefaultElement -> "Plaintext", + DefaultElement -> "Data", BinaryFormat -> False, Options -> { "CharacterEncoding", diff --git a/mathics/autoload/formats/CSV/Import.m b/mathics/SystemFiles/Formats/CSV/Import.wl similarity index 91% rename from mathics/autoload/formats/CSV/Import.m rename to mathics/SystemFiles/Formats/CSV/Import.wl index 753b0b938..cf5f6e646 100644 --- a/mathics/autoload/formats/CSV/Import.m +++ b/mathics/SystemFiles/Formats/CSV/Import.wl @@ -1,3 +1,5 @@ +(* ::Package:: *) + (* CSV Importer *) Begin["System`Convert`TableDump`"] @@ -23,12 +25,13 @@ System`Convert`TableDump`ImportCSV, { "Data" :> GetData, - "Grid" :> GetGrid + "Grid" :> GetGrid }, (* Sources -> ImportExport`DefaultSources["Table"], *) FunctionChannels -> {"Streams"}, AvailableElements -> {"Data", "Grid"}, DefaultElement -> "Data", + BinaryFormat -> False, Options -> { "CharacterEncoding", "FieldSeparators" diff --git a/mathics/autoload/formats/HTML/Import.m b/mathics/SystemFiles/Formats/HTML/Import.wl similarity index 100% rename from mathics/autoload/formats/HTML/Import.m rename to mathics/SystemFiles/Formats/HTML/Import.wl diff --git a/mathics/autoload/formats/Image/Export.m b/mathics/SystemFiles/Formats/Image/Export.m similarity index 100% rename from mathics/autoload/formats/Image/Export.m rename to mathics/SystemFiles/Formats/Image/Export.m diff --git a/mathics/autoload/formats/Image/Import.m b/mathics/SystemFiles/Formats/Image/Import.wl similarity index 100% rename from mathics/autoload/formats/Image/Import.m rename to mathics/SystemFiles/Formats/Image/Import.wl diff --git a/mathics/autoload/formats/JSON/Import.m b/mathics/SystemFiles/Formats/JSON/Import.wl similarity index 100% rename from mathics/autoload/formats/JSON/Import.m rename to mathics/SystemFiles/Formats/JSON/Import.wl diff --git a/mathics/autoload/formats/PACKAGE/Import.m b/mathics/SystemFiles/Formats/PACKAGE/Import.wl similarity index 100% rename from mathics/autoload/formats/PACKAGE/Import.m rename to mathics/SystemFiles/Formats/PACKAGE/Import.wl diff --git a/mathics/autoload/formats/SVG/Export.m b/mathics/SystemFiles/Formats/SVG/Export.wl similarity index 91% rename from mathics/autoload/formats/SVG/Export.m rename to mathics/SystemFiles/Formats/SVG/Export.wl index a57b5ba95..a705e37f2 100644 --- a/mathics/autoload/formats/SVG/Export.m +++ b/mathics/SystemFiles/Formats/SVG/Export.wl @@ -3,8 +3,8 @@ Begin["System`Convert`TextDump`"] -SVGExport[filename_, expr_, opts___] := - Module[{strm, data, p, q, expr2}, +SVGExport[filename_, expr_, opts___] := + Module[{strm, data, p, q, expr2}, strm = OpenWrite[filename]; If[strm === $Failed, Return[$Failed]]; expr2 = If[Head[expr]=!=System`Graphics, System`Graphics[{System`Inset[ToString[expr]]}], expr]; diff --git a/mathics/autoload/formats/Text/Export.m b/mathics/SystemFiles/Formats/Text/Export.m similarity index 100% rename from mathics/autoload/formats/Text/Export.m rename to mathics/SystemFiles/Formats/Text/Export.m diff --git a/mathics/autoload/formats/Text/Import.m b/mathics/SystemFiles/Formats/Text/Import.wl similarity index 100% rename from mathics/autoload/formats/Text/Import.m rename to mathics/SystemFiles/Formats/Text/Import.wl diff --git a/mathics/autoload/formats/XML/Import.m b/mathics/SystemFiles/Formats/XML/Import.wl similarity index 100% rename from mathics/autoload/formats/XML/Import.m rename to mathics/SystemFiles/Formats/XML/Import.wl diff --git a/mathics/SystemFiles/Formats/ZIP/Import.wl b/mathics/SystemFiles/Formats/ZIP/Import.wl new file mode 100644 index 000000000..b3bce484d --- /dev/null +++ b/mathics/SystemFiles/Formats/ZIP/Import.wl @@ -0,0 +1,12 @@ +(* ZIP compressed file and file archive Importer *) + +Begin["System`ZIP`"] +ImportExport`RegisterImport[ + "ZIP", + ImportZip, + FunctionChannels -> {"Streams"}, + AvailableElements -> {"FileNames", "Summary"}, + DefaultElement -> "FileNames" +] + +End[] diff --git a/mathics/builtin/files_io/files.py b/mathics/builtin/files_io/files.py index f6b8f0dad..5d2cf9db8 100644 --- a/mathics/builtin/files_io/files.py +++ b/mathics/builtin/files_io/files.py @@ -270,7 +270,6 @@ class FilePrint(Builtin): expected_args = (1, 2) messages = { "zstr": ("The file name cannot be an empty string."), - "badfile": ("The specified argument, `1`, should be a valid string."), } options = { @@ -283,6 +282,7 @@ class FilePrint(Builtin): def eval(self, path, evaluation: Evaluation, options: dict): "FilePrint[path_, OptionsPattern[FilePrint]]" + # TODO also check for File. if not isinstance(path, String): evaluation.message("FilePrint", "badfile", path) return @@ -675,7 +675,7 @@ def eval_input(self, exprs, name, n, evaluation: Evaluation): stream = stream_manager.lookup_stream(n.get_int_value()) if stream is None or stream.io.closed: - evaluation.message("Put", "openx", get_eval_Expression()) + evaluation.message("Put", "openx", evaluation.current_expression) return # In Mathics3-server, evaluation.format_output is modified. @@ -772,7 +772,7 @@ def eval_input(self, exprs, name, n, evaluation: Evaluation): stream = stream_manager.lookup_stream(n.get_int_value()) if stream is None or stream.io.closed: - evaluation.message("Put", "openx", get_eval_Expression()) + evaluation.message("Put", "openx", evaluation.current_expression) return text = [ @@ -1187,7 +1187,7 @@ def eval_n(self, file, types, n: Integer, evaluation: Evaluation, options: dict) py_n = n.value if py_n < 0: - evaluation.message("ReadList", "intnm", get_eval_Expression()) + evaluation.message("ReadList", "intnm", evaluation.current_expression) return result = [] @@ -1298,7 +1298,9 @@ def eval_input(self, name, n, m, evaluation): seekpos = m.to_python() if not (isinstance(seekpos, int) or seekpos == float("inf")): - evaluation.message("SetStreamPosition", "stmrng", get_eval_Expression(), m) + evaluation.message( + "SetStreamPosition", "stmrng", evaluation.current_expression, m + ) return try: diff --git a/mathics/builtin/files_io/filesystem.py b/mathics/builtin/files_io/filesystem.py index 00cd94b70..70578f2d8 100644 --- a/mathics/builtin/files_io/filesystem.py +++ b/mathics/builtin/files_io/filesystem.py @@ -9,7 +9,7 @@ import pathlib import re import shutil -from typing import List +from typing import List, Optional from mathics.builtin.files_io.files import Mathics3Open from mathics.core.atoms import Integer, String @@ -20,7 +20,7 @@ from mathics.core.convert.regex import to_regex from mathics.core.evaluation import Evaluation from mathics.core.expression import Expression -from mathics.core.streams import create_temporary_file, path_search, urlsave_tmp +from mathics.core.streams import create_temporary_file, urlsave_tmp from mathics.core.symbols import ( Symbol, SymbolFalse, @@ -36,6 +36,7 @@ ) from mathics.eval.directories import DIRECTORY_STACK from mathics.eval.files_io.files import eval_Get +from mathics.eval.files_io.filesystem import eval_FindFile from mathics.eval.stackframe import get_eval_Expression @@ -62,21 +63,20 @@ def eval(self, name, evaluation): py_name = name.to_python() if not isinstance(py_name, str): - evaluation.message("AbsoluteFileName", "fstr", name) + evaluation.message("AbsoluteFileName", "badfile", name) return if py_name[0] == py_name[-1] == '"': py_name = py_name[1:-1] - result, _ = path_search(py_name) - + result = eval_FindFile(py_name) if result is None: evaluation.message( "AbsoluteFileName", "nffil", to_expression("AbsoluteFileName", name) ) return SymbolFailed - return String(osp.abspath(result)) + return result class CopyDirectory(Builtin): @@ -173,7 +173,7 @@ def eval(self, source, dest, evaluation): if py_dest[0] == py_dest[-1] == '"': py_dest = py_dest[1:-1] - py_source, _ = path_search(py_source) + py_source = eval_FindFile(py_source) if py_source is None: evaluation.message("CopyFile", "filex", source) @@ -184,7 +184,7 @@ def eval(self, source, dest, evaluation): return SymbolFailed try: - shutil.copy(py_source, py_dest) + shutil.copy(py_source.value, py_dest) except IOError: evaluation.message("CopyFile", "nffil", get_eval_Expression()) return SymbolFailed @@ -300,12 +300,12 @@ def eval(self, filename, evaluation): if path[0] == path[-1] == '"': path = path[1:-1] - path, _ = path_search(path) + resolved_path = eval_FindFile(path) - if path is None: + if resolved_path is None: evaluation.message("DeleteFile", "nffil", get_eval_Expression()) return SymbolFailed - py_paths.append(path) + py_paths.append(resolved_path.value) for path in py_paths: try: @@ -355,7 +355,6 @@ class DirectoryStack(Builtin): def eval(self, evaluation): "DirectoryStack[]" - global DIRECTORY_STACK return from_python(DIRECTORY_STACK) @@ -504,7 +503,7 @@ class FileExistsQ(Builtin): } summary_text = "test whether a file exists" - def eval(self, filename, evaluation): + def eval(self, filename, evaluation) -> Optional[Symbol]: "FileExistsQ[filename_]" path = filename.to_python() if not (isinstance(path, str) and path[0] == path[-1] == '"'): @@ -512,11 +511,8 @@ def eval(self, filename, evaluation): return path = path[1:-1] - path, is_temporary_file = path_search(path) - - if path is None: - return SymbolFalse - return SymbolTrue + resolved_path = eval_FindFile(path) + return SymbolFalse if resolved_path is None else SymbolTrue class FileExtension(Builtin): @@ -606,12 +602,10 @@ def eval(self, name, evaluation): return py_name = py_name[1:-1] - result, is_temporary_file = path_search(py_name) - + result = eval_FindFile(py_name) if result is None: return SymbolFailed - - return String(osp.abspath(result)) + return result class FileNames(Builtin): @@ -983,9 +977,9 @@ def eval(self, source, dest, evaluation): py_source = py_source[1:-1] py_dest = py_dest[1:-1] - py_source, _ = path_search(py_source) + resolved_source = eval_FindFile(py_source) - if py_source is None: + if resolved_source is None: evaluation.message("RenameFile", "filex", source) return SymbolFailed @@ -994,7 +988,7 @@ def eval(self, source, dest, evaluation): return SymbolFailed try: - shutil.move(py_source, py_dest) + shutil.move(resolved_source.value, py_dest) except IOError: evaluation.message("RenameFile", "nffil", dest) return SymbolFailed diff --git a/mathics/builtin/files_io/importexport.py b/mathics/builtin/files_io/importexport.py index 7ce7aa028..8f03c2eef 100644 --- a/mathics/builtin/files_io/importexport.py +++ b/mathics/builtin/files_io/importexport.py @@ -38,19 +38,21 @@ SymbolByteArray, SymbolFailed, SymbolFileExtension, - SymbolFileFormat, - SymbolFindFile, SymbolOpenWrite, SymbolOutputStream, SymbolToString, ) from mathics.eval.files_io.files import eval_Close +from mathics.eval.files_io.filesystem import eval_FindFile from mathics.eval.files_io.importexport import ( IMPORTERS, MIMETYPE_TO_SHORTNAME, + eval_FileFormat, eval_Import, + eval_Import_Elements, + eval_JSONImport, + eval_ZIPImport, filetype_from_mime_content, - filetype_from_path, importer_exporter_options, ) @@ -874,7 +876,7 @@ def evaluate(self, evaluation: Evaluation): class RegisterImport(Builtin): """ - ## :internal native symbol: + :WMA link:https://reference.wolfram.com/language/tutorial/ImportingAndExporting.html#138898786
'RegisterImport'["$format$", $defaultFunction$] @@ -978,7 +980,9 @@ class RegisterImport(Builtin): } summary_text = "register an importer for a file format" - def eval(self, formatname, function, posts, evaluation: Evaluation, options): + def eval( + self, formatname: String, function, posts, evaluation: Evaluation, options + ): """ImportExport`RegisterImport[formatname_String, function_, posts_, OptionsPattern[ImportExport`RegisterImport]]""" @@ -1208,43 +1212,102 @@ class Import(Builtin): summary_text = "import elements from a file" - def eval(self, source, evaluation, options={}): - "Import[source_, OptionsPattern[]]" - return self.eval_elements(source, ListExpression(), evaluation, options) - - def eval_element(self, source, element: String, evaluation, options={}): - "Import[source_, element_String, OptionsPattern[]]" - return self.eval_elements(source, ListExpression(element), evaluation, options) - - def eval_elements(self, source, elements, evaluation, options={}): - "Import[source_, elements_List?(AllTrue[#, NotOptionQ]&), OptionsPattern[]]" + def import_setup(self, source, evaluation) -> tuple: # Check filename path = source.to_python() if not (isinstance(path, str) and path[0] == path[-1] == '"'): evaluation.message("Import", "chtype", source) - return SymbolFailed + return SymbolFailed, None # Load local file - findfile = Expression(SymbolFindFile, source).evaluate(evaluation) + if path[0] == path[-1] == '"': + path = path[1:-1] + findfile = eval_FindFile(path) - if findfile is SymbolFailed: + if findfile is None: evaluation.message("Import", "nffil") - return findfile + return SymbolFailed, None - data = ( - Expression(SymbolFileFormat, findfile) - .evaluate(evaluation=evaluation) - .get_string_value() - ) + return findfile, eval_FileFormat(findfile.value).value + + def eval(self, source, evaluation, options={}): + "Import[source_, OptionsPattern[]]" + return self.eval_element_list(source, ListExpression(), evaluation, options) + + def eval_elements_query(self, source, evaluation, options={}): + """Import[source_, "Elements", OptionsPattern[]]""" + _, file_format = self.import_setup(source, evaluation) + return eval_Import_Elements(file_format, evaluation) + + def eval_fmt(self, source, fmt: String, evaluation, options={}): + "Import[source_, fmt_String, OptionsPattern[]]" + return self.eval_element_list(source, ListExpression(fmt), evaluation, options) + + def eval_element_list(self, source, elements, evaluation, options={}): + "Import[source_, elements_List?(AllTrue[#, NotOptionQ]&), OptionsPattern[]]" + + findfile, data = self.import_setup(source, evaluation) + if findfile is SymbolFailed: + return SymbolFailed def determine_filetype(data: str) -> str: return data return eval_Import( - findfile, determine_filetype, elements, evaluation, options, data=data + findfile, determine_filetype, elements, evaluation, options, data ) +class ImportJSON(Builtin): + """ + ## :trace native symbol: + +
+
'ImportJSON[path]' +
Return file archived under $path$ + +
'ImportJSON[path, "Elements"]' +
Returns elements of ZIP file {"FileName", "Summary"}. +
+ + """ + + summary_text = "import ZIP file" + + def eval(self, path: String, evaluation: Evaluation): + "%(name)s[path_String]" + return eval_JSONImport(path.value) + + def eval_elements(self, path: String, evaluation: Evaluation): + """%(name)s[path_String, "Elements"]""" + return eval_JSONImport(path.value) + + +class ImportZip(Builtin): + """ + ## :trace native symbol: + +
+
'ImportZip[path]' +
Return file archived under $path$ + +
'ImportZip[path, "Elements"]' +
Returns elements of ZIP file {"FileName", "Summary"}. +
+ + """ + + summary_text = "import ZIP file" + + def eval(self, path: String, evaluation: Evaluation): + "%(name)s[path_String]" + return eval_ZIPImport(path.value) + + def eval_elements(self, path: String, evaluation: Evaluation): + """%(name)s[path_String, "Elements"]""" + return eval_Import_Elements("ZIP", evaluation) + + class ImportString(Builtin): """ @@ -1686,14 +1749,17 @@ class FileFormat(Builtin): def eval(self, filename: String, evaluation: Evaluation): "FileFormat[filename_String]" - findfile = Expression(SymbolFindFile, filename).evaluate(evaluation) - if findfile is SymbolFailed: - evaluation.message( - "FileFormat", "nffil", Expression(SymbolFileFormat, filename) - ) - return findfile + py_name = filename.value + if py_name[0] == filename.value[-1] == '"': + py_name = py_name[1:-1] + + resolved_path = eval_FindFile(py_name) + + if resolved_path is None: + evaluation.message("FileFormat", "nffil", evaluation.current_expression) + return SymbolFailed - return String(filetype_from_path(findfile.value)) + return eval_FileFormat(resolved_path.value) class B64Decode(Builtin): diff --git a/mathics/builtin/messages.py b/mathics/builtin/messages.py index dc569fe8b..ae9bdc0c4 100644 --- a/mathics/builtin/messages.py +++ b/mathics/builtin/messages.py @@ -182,6 +182,7 @@ class General(Builtin): "`1` called with `2` arguments; " "`3` or `4` arguments are expected." ), "argtu": ("`1` called with 1 argument; `2` or `3` arguments are expected."), + "badfile": ("The specified argument, `1`, should be a valid string or File."), "base": "Requested base `1` in `2` should be between 2 and `3`.", "boxfmt": "`1` is not a box formatting type.", "charcode": "The character encoding `1` is not supported. Use $CharacterEncodings to list supported encodings.", diff --git a/mathics/core/definitions.py b/mathics/core/definitions.py index 47355204f..5d5189750 100644 --- a/mathics/core/definitions.py +++ b/mathics/core/definitions.py @@ -1127,3 +1127,4 @@ def load_builtin_definitions( pickle.dump(self.builtin, builtin_file, -1) autoload_files(self, ROOT_DIR, "autoload") + autoload_files(self, osp.join(ROOT_DIR, "SystemFiles"), "Formats") diff --git a/mathics/eval/files_io/filesystem.py b/mathics/eval/files_io/filesystem.py new file mode 100644 index 000000000..19a20f561 --- /dev/null +++ b/mathics/eval/files_io/filesystem.py @@ -0,0 +1,22 @@ +import os.path as osp +from typing import Optional + +from mathics.core.atoms import String +from mathics.core.streams import path_search + + +def eval_FindFile(name: str) -> Optional[String]: + """Underlying implemenation for FindFile[name_String], but other builtins + need this kind of function + Searches for "name" and returns a String value for its absolute path. + If "name" can't be found, return None. + The caller will be responsible for deciding what to do, e.g., + return $SymbolFailed, or do that with an error message, or ignore. + """ + + result, _ = path_search(name) + + if result is None: + return None + + return String(osp.abspath(result)) diff --git a/mathics/eval/files_io/importexport.py b/mathics/eval/files_io/importexport.py index c5ec516c7..c409acda1 100644 --- a/mathics/eval/files_io/importexport.py +++ b/mathics/eval/files_io/importexport.py @@ -3,13 +3,16 @@ file path. """ +import json import mimetypes import os.path as osp +import zipfile from itertools import chain from typing import Dict, Final, Optional -from mathics.core.atoms import ByteArray -from mathics.core.builtin import String, get_option +from mathics.core.atoms import ByteArray, String +from mathics.core.builtin import get_option +from mathics.core.convert.expression import to_mathics_list from mathics.core.convert.python import from_python from mathics.core.expression import Expression from mathics.core.list import ListExpression @@ -194,6 +197,13 @@ def importer_exporter_options( return stream_options, custom_options +def eval_FileFormat(path: str) -> String: + """ + Basic implemenation beind FileFormat[filename]. + """ + return String(filetype_from_path(path)) + + def eval_Import( findfile: Optional[String], determine_filetype, @@ -202,6 +212,9 @@ def eval_Import( options, data: Optional[str], ): + """ + Basic implemenation beind Import[]. + """ current_predetermined_out = evaluation.predetermined_out # Check elements if elements.has_form("List", None): @@ -380,6 +393,81 @@ def eval_Import( return SymbolFailed +def eval_Import_Elements(file_format: str, evaluation): + """ + Basic implemenation beind Import[xxx, Elements]. + """ + filetype = MIME_SHORTNAME_TO_WMA.get(file_format, file_format) + + if filetype not in IMPORTERS.keys(): + evaluation.message("Import", "fmtnosup", String(filetype)) + return SymbolFailed + + # Get information from the registered Importer. + # In this we've registered, the field names that can be asked for + # under the option "Elements". + _, _, _, options = IMPORTERS[filetype] + return options.get("System`AvailableElements") + + +# FIXME: +# We should not be extracting everything and returning a list of rules. +# provide a better interface. +def eval_JSONImport(json_path: str) -> ListExpression: + """Takes a ZIP file path and returns a list of file names/paths contained inside.""" + with open(json_path, "r") as json_file: + json_data = json.load(json_file) + mathics_json = from_python(json_data) + exprs = [ + Expression( + SymbolRule, + String("Data"), + mathics_json, + ), + Expression( + SymbolRule, + String("Dataset"), + mathics_json, + ), + ] + return ListExpression(*exprs) + + +# FIXME: +# We should not be extracting everything and returning a list of rules. +# provide a better interface. +def eval_ZIPImport(zip_path: str) -> ListExpression: + """Takes a ZIP file path and returns a list of file names/paths contained inside.""" + with zipfile.ZipFile(zip_path, "r") as archive: + # FIXME: Using "filenames" for "Summary" items is not quite right. + filenames = archive.namelist() + mathics_filenames = to_mathics_list(*filenames) + exprs = [ + Expression( + SymbolRule, + String("FileNames"), + mathics_filenames, + ), + Expression( + SymbolRule, + String("Summary"), + mathics_filenames, + ), + ] + + if filenames: + for filename in filenames: + exprs.append( + Expression( + SymbolRule, + String(filename), + String(archive.read(filename).decode("utf-8")), + ) + ) + + return ListExpression(*exprs) + + def get_results( tmp_function, findfile: Optional[String], diff --git a/mathics/session.py b/mathics/session.py index 162ef9c03..15257bd14 100644 --- a/mathics/session.py +++ b/mathics/session.py @@ -44,7 +44,9 @@ def autoload_files( py_encoding = encoding.value if isinstance(encoding, String) else "UTF-8" for root, _, files in os.walk(osp_join(root_dir_path, autoload_dir)): - for path in [osp_join(root, f) for f in files if f.endswith(".m")]: + for path in [ + osp_join(root, f) for f in files if f.endswith(".m") or f.endswith(".wl") + ]: # Autoload definitions should be go in the System context # by default, rather than the Global context. defs.set_current_context("System`") diff --git a/pyproject.toml b/pyproject.toml index 449c7ae57..9921f95f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,11 +113,11 @@ include = ["mathics*"] "mathics" = [ "Packages/*/*.m", "Packages/*/Kernel/init.m", + "SystemFiles/Dictionaries/*/*.txt", + "SystemFiles/Formats/*/*.wl", "autoload-cli/*.m", "autoload/*.m", "autoload/*/*.m", - "autoload/formats/*/Export.m", - "autoload/formats/*/Import.m", "data/*.csv", "data/*.json", "data/*.pcl", diff --git a/test/builtin/files_io/test_files.py b/test/builtin/files_io/test_files.py index deb73a885..b47f5c987 100644 --- a/test/builtin/files_io/test_files.py +++ b/test/builtin/files_io/test_files.py @@ -111,7 +111,7 @@ def test_close(): ('Close["abc"]', ("abc is not open.",), "Close[abc]", ""), ( "exp = Sin[1]; FilePrint[exp]", - ("The specified argument, Sin[1], should be a valid string.",), + ("The specified argument, Sin[1], should be a valid string or File.",), "FilePrint[Sin[1]]", "", ),