From f289c1cc2b9d3fc3189931bba4c1ca51a67f5050 Mon Sep 17 00:00:00 2001 From: rocky Date: Thu, 11 Jun 2026 22:05:17 -0400 Subject: [PATCH 01/11] First cut at zip imports --- mathics/builtin/files_io/importexport.py | 19 +++++++++++++++++++ mathics/eval/files_io/importexport.py | 12 ++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/mathics/builtin/files_io/importexport.py b/mathics/builtin/files_io/importexport.py index 7ce7aa028..6bb9f0ed1 100644 --- a/mathics/builtin/files_io/importexport.py +++ b/mathics/builtin/files_io/importexport.py @@ -49,6 +49,7 @@ IMPORTERS, MIMETYPE_TO_SHORTNAME, eval_Import, + eval_ZIPImport, filetype_from_mime_content, filetype_from_path, importer_exporter_options, @@ -1245,6 +1246,24 @@ def determine_filetype(data: str) -> str: ) +class ImportZip(Builtin): + """ + ## :trace native symbol: + +
+
'ImportZip[path]' +
Return file archived under $path$ +
+ + """ + + summary_text = "import ZIP file" + + def eval(self, path: String, evaluation: Evaluation): + "%(name)s[path_String]" + return eval_ZIPImport(path) + + class ImportString(Builtin): """ diff --git a/mathics/eval/files_io/importexport.py b/mathics/eval/files_io/importexport.py index c5ec516c7..cbf9c374d 100644 --- a/mathics/eval/files_io/importexport.py +++ b/mathics/eval/files_io/importexport.py @@ -5,11 +5,13 @@ 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 @@ -380,6 +382,12 @@ def eval_Import( return SymbolFailed +def eval_ZIPImport(zip_path: String) -> ListExpression: + """Takes a ZIP file path and returns a list of file names/paths contained inside.""" + with zipfile.ZipFile(zip_path.value, "r") as archive: + return to_mathics_list(*archive.namelist()) + + def get_results( tmp_function, findfile: Optional[String], From c502215a612c0a46127c2951a06dbaa358dbff23 Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 13 Jun 2026 11:26:54 -0400 Subject: [PATCH 02/11] Start adding Import --- mathics/autoload/formats/ZIP/Import.wl | 11 +++++ mathics/builtin/files_io/importexport.py | 36 +++++++++++++++- mathics/eval/files_io/importexport.py | 55 +++++++++++++++++++++++- mathics/session.py | 4 +- pyproject.toml | 2 + 5 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 mathics/autoload/formats/ZIP/Import.wl diff --git a/mathics/autoload/formats/ZIP/Import.wl b/mathics/autoload/formats/ZIP/Import.wl new file mode 100644 index 000000000..7a7fa98ec --- /dev/null +++ b/mathics/autoload/formats/ZIP/Import.wl @@ -0,0 +1,11 @@ +(* ZIP compressed file and file archive Importer *) + +Begin["System`ZIP`"] +ImportExport`RegisterImport[ + "ZIP", + ImportZip, + AvailableElements -> {"FileNames", "Summary"}, + DefaultElement -> "FileNames" +] + +End[] diff --git a/mathics/builtin/files_io/importexport.py b/mathics/builtin/files_io/importexport.py index 6bb9f0ed1..e76a25aaf 100644 --- a/mathics/builtin/files_io/importexport.py +++ b/mathics/builtin/files_io/importexport.py @@ -49,6 +49,7 @@ IMPORTERS, MIMETYPE_TO_SHORTNAME, eval_Import, + eval_JSONImport, eval_ZIPImport, filetype_from_mime_content, filetype_from_path, @@ -1219,6 +1220,7 @@ def eval_element(self, source, element: String, evaluation, options={}): def eval_elements(self, source, elements, evaluation, options={}): "Import[source_, elements_List?(AllTrue[#, NotOptionQ]&), OptionsPattern[]]" + # Check filename path = source.to_python() if not (isinstance(path, str) and path[0] == path[-1] == '"'): @@ -1246,6 +1248,31 @@ def determine_filetype(data: str) -> str: ) +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: @@ -1253,6 +1280,9 @@ class ImportZip(Builtin):
'ImportZip[path]'
Return file archived under $path$ + +
'ImportZip[path, "Elements"]' +
Returns elements of ZIP file {"FileName", "Summary"}.
""" @@ -1261,7 +1291,11 @@ class ImportZip(Builtin): def eval(self, path: String, evaluation: Evaluation): "%(name)s[path_String]" - return eval_ZIPImport(path) + return eval_ZIPImport(path.value) + + def eval_elements(self, path: String, evaluation: Evaluation): + """%(name)s[path_String]["Elements"]""" + return eval_ZIPImport(path.value) class ImportString(Builtin): diff --git a/mathics/eval/files_io/importexport.py b/mathics/eval/files_io/importexport.py index cbf9c374d..df283f3ee 100644 --- a/mathics/eval/files_io/importexport.py +++ b/mathics/eval/files_io/importexport.py @@ -3,6 +3,7 @@ file path. """ +import json import mimetypes import os.path as osp import zipfile @@ -382,10 +383,62 @@ def eval_Import( return SymbolFailed +# 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: String) -> ListExpression: """Takes a ZIP file path and returns a list of file names/paths contained inside.""" with zipfile.ZipFile(zip_path.value, "r") as archive: - return to_mathics_list(*archive.namelist()) + # 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( 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..0e0257dca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -117,7 +117,9 @@ include = ["mathics*"] "autoload/*.m", "autoload/*/*.m", "autoload/formats/*/Export.m", + "autoload/formats/*/Export.wl", "autoload/formats/*/Import.m", + "autoload/formats/*/Import.wl", "data/*.csv", "data/*.json", "data/*.pcl", From c505f94f0a6a69914edd2e24d61e624c475d4a6a Mon Sep 17 00:00:00 2001 From: rocky Date: Sun, 14 Jun 2026 21:46:18 -0400 Subject: [PATCH 03/11] Start redoing Import Add eval_FindFile and eval_FileFormat. Use these instead of Expression().evaluate --- mathics/builtin/files_io/files.py | 12 ++++--- mathics/builtin/files_io/filesystem.py | 44 ++++++++++-------------- mathics/builtin/files_io/importexport.py | 38 ++++++++++---------- mathics/builtin/messages.py | 1 + mathics/eval/files_io/filesystem.py | 22 ++++++++++++ mathics/eval/files_io/importexport.py | 10 ++++++ test/builtin/files_io/test_files.py | 2 +- 7 files changed, 79 insertions(+), 50 deletions(-) create mode 100644 mathics/eval/files_io/filesystem.py 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 e76a25aaf..8fbc65754 100644 --- a/mathics/builtin/files_io/importexport.py +++ b/mathics/builtin/files_io/importexport.py @@ -38,21 +38,20 @@ 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_JSONImport, eval_ZIPImport, filetype_from_mime_content, - filetype_from_path, importer_exporter_options, ) @@ -1228,23 +1227,21 @@ def eval_elements(self, source, elements, evaluation, options={}): return SymbolFailed # 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 - data = ( - Expression(SymbolFileFormat, findfile) - .evaluate(evaluation=evaluation) - .get_string_value() - ) + data = eval_FileFormat(findfile.value).value 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 ) @@ -1739,14 +1736,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/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 df283f3ee..31287db87 100644 --- a/mathics/eval/files_io/importexport.py +++ b/mathics/eval/files_io/importexport.py @@ -197,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, @@ -205,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): 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]]", "", ), From 8dbe88d6d4d28a164fe0158ad8f8c02d0107623a Mon Sep 17 00:00:00 2001 From: rocky Date: Mon, 15 Jun 2026 09:15:31 -0400 Subject: [PATCH 04/11] Start to segregate and customize Import signatures --- mathics/builtin/files_io/importexport.py | 42 +++++++++----- mathics/eval/files_io/importexport.py | 74 ++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 14 deletions(-) diff --git a/mathics/builtin/files_io/importexport.py b/mathics/builtin/files_io/importexport.py index 8fbc65754..904e08fb3 100644 --- a/mathics/builtin/files_io/importexport.py +++ b/mathics/builtin/files_io/importexport.py @@ -49,6 +49,7 @@ MIMETYPE_TO_SHORTNAME, eval_FileFormat, eval_Import, + eval_Import_Elements, eval_JSONImport, eval_ZIPImport, filetype_from_mime_content, @@ -1209,22 +1210,12 @@ 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 if path[0] == path[-1] == '"': @@ -1233,9 +1224,32 @@ def eval_elements(self, source, elements, evaluation, options={}): if findfile is None: evaluation.message("Import", "nffil") - return SymbolFailed + return SymbolFailed, None + + return findfile, eval_FileFormat(findfile.value).value - data = 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[]]""" + _, format = self.import_setup(source, evaluation) + return self.eval_element_list( + source, ListExpression(String("Elements")), evaluation, options + ) + # return eval_Import_Elements(source, format, evaluation, options) + + 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 diff --git a/mathics/eval/files_io/importexport.py b/mathics/eval/files_io/importexport.py index 31287db87..bf2a8df2d 100644 --- a/mathics/eval/files_io/importexport.py +++ b/mathics/eval/files_io/importexport.py @@ -393,6 +393,39 @@ def eval_Import( return SymbolFailed +def eval_Import_Elements(findfile: String, format: str, evaluation, options): + """ + Basic implemenation beind Import[xxx, Eelements]. + """ + filetype = MIME_SHORTNAME_TO_WMA.get(format, format) + + if filetype not in IMPORTERS.keys(): + evaluation.message("Import", "fmtnosup", String(filetype)) + return SymbolFailed + + # Load the importer + _, import_function, _, importer_options = IMPORTERS[filetype] + + _, custom_options = importer_exporter_options( + importer_options.get("System`Options"), options, "System`Import", evaluation + ) + + # Perform the import + + results = get_elements_names( + import_function, + findfile, + custom_options, + evaluation, + options, + ) + if results is None: + return SymbolFailed + elif results is SymbolFailed: + return SymbolFailed + return to_mathics_list(*results) + + # FIXME: # We should not be extracting everything and returning a list of rules. # provide a better interface. @@ -451,6 +484,47 @@ def eval_ZIPImport(zip_path: String) -> ListExpression: return ListExpression(*exprs) +def get_elements_names( + import_function, + findfile: String, + custom_options, + evaluation, + options, +): + """ + Retrieves field names in Import[xxx, Elements] + """ + mode = "r" + if options.get("System`BinaryFormat") is SymbolTrue: + if not mode.endswith("b"): + mode += "b" + + encoding_option = options.get("System`CharacterEncoding") + encoding = encoding_option.value if isinstance(encoding_option, String) else None + + stream = eval_Open( + name=findfile, + mode=mode, + stream_type="InputStream", + encoding=encoding, + evaluation=evaluation, + ) + if stream is None: + return + + # FIXME: do this in a better way without having to call Expression, or even having + # to read the entire file! + tmp = Expression(import_function, stream, *custom_options).evaluate(evaluation) + eval_Close(stream, evaluation) + tmp = tmp.get_elements() + if not all(expr.has_form("Rule", None) for expr in tmp): + return None + + # Return just the column names. a is a tuple and has has the actual + # value. + return [a[0].value for a in (x.get_elements() for x in tmp)] + + def get_results( tmp_function, findfile: Optional[String], From 77512bbf7b736b72cc0feae446e5f1023d6e159c Mon Sep 17 00:00:00 2001 From: rocky Date: Mon, 15 Jun 2026 19:29:54 -0400 Subject: [PATCH 05/11] Revise Import[xxx, "Elements"] Import[xxx, "Elements"] now gets field names from RegisterImport's AvailableElements field. --- mathics/autoload/formats/CSV/Import.m | 2 +- mathics/builtin/files_io/importexport.py | 11 ++-- mathics/eval/files_io/importexport.py | 73 +++--------------------- 3 files changed, 14 insertions(+), 72 deletions(-) diff --git a/mathics/autoload/formats/CSV/Import.m b/mathics/autoload/formats/CSV/Import.m index 753b0b938..945865d2a 100644 --- a/mathics/autoload/formats/CSV/Import.m +++ b/mathics/autoload/formats/CSV/Import.m @@ -23,7 +23,7 @@ System`Convert`TableDump`ImportCSV, { "Data" :> GetData, - "Grid" :> GetGrid + "Grid" :> GetGrid }, (* Sources -> ImportExport`DefaultSources["Table"], *) FunctionChannels -> {"Streams"}, diff --git a/mathics/builtin/files_io/importexport.py b/mathics/builtin/files_io/importexport.py index 904e08fb3..b3ce3b14d 100644 --- a/mathics/builtin/files_io/importexport.py +++ b/mathics/builtin/files_io/importexport.py @@ -980,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]]""" @@ -1234,11 +1236,8 @@ def eval(self, source, evaluation, options={}): def eval_elements_query(self, source, evaluation, options={}): """Import[source_, "Elements", OptionsPattern[]]""" - _, format = self.import_setup(source, evaluation) - return self.eval_element_list( - source, ListExpression(String("Elements")), evaluation, options - ) - # return eval_Import_Elements(source, format, evaluation, options) + _, 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[]]" diff --git a/mathics/eval/files_io/importexport.py b/mathics/eval/files_io/importexport.py index bf2a8df2d..4a0e15b9a 100644 --- a/mathics/eval/files_io/importexport.py +++ b/mathics/eval/files_io/importexport.py @@ -393,37 +393,21 @@ def eval_Import( return SymbolFailed -def eval_Import_Elements(findfile: String, format: str, evaluation, options): +def eval_Import_Elements(file_format: str, evaluation): """ - Basic implemenation beind Import[xxx, Eelements]. + Basic implemenation beind Import[xxx, Elements]. """ - filetype = MIME_SHORTNAME_TO_WMA.get(format, format) + filetype = MIME_SHORTNAME_TO_WMA.get(file_format, file_format) if filetype not in IMPORTERS.keys(): evaluation.message("Import", "fmtnosup", String(filetype)) return SymbolFailed - # Load the importer - _, import_function, _, importer_options = IMPORTERS[filetype] - - _, custom_options = importer_exporter_options( - importer_options.get("System`Options"), options, "System`Import", evaluation - ) - - # Perform the import - - results = get_elements_names( - import_function, - findfile, - custom_options, - evaluation, - options, - ) - if results is None: - return SymbolFailed - elif results is SymbolFailed: - return SymbolFailed - return to_mathics_list(*results) + # 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: @@ -484,47 +468,6 @@ def eval_ZIPImport(zip_path: String) -> ListExpression: return ListExpression(*exprs) -def get_elements_names( - import_function, - findfile: String, - custom_options, - evaluation, - options, -): - """ - Retrieves field names in Import[xxx, Elements] - """ - mode = "r" - if options.get("System`BinaryFormat") is SymbolTrue: - if not mode.endswith("b"): - mode += "b" - - encoding_option = options.get("System`CharacterEncoding") - encoding = encoding_option.value if isinstance(encoding_option, String) else None - - stream = eval_Open( - name=findfile, - mode=mode, - stream_type="InputStream", - encoding=encoding, - evaluation=evaluation, - ) - if stream is None: - return - - # FIXME: do this in a better way without having to call Expression, or even having - # to read the entire file! - tmp = Expression(import_function, stream, *custom_options).evaluate(evaluation) - eval_Close(stream, evaluation) - tmp = tmp.get_elements() - if not all(expr.has_form("Rule", None) for expr in tmp): - return None - - # Return just the column names. a is a tuple and has has the actual - # value. - return [a[0].value for a in (x.get_elements() for x in tmp)] - - def get_results( tmp_function, findfile: Optional[String], From 2fc493cb6e3bcd563983b3f99186da3509378def Mon Sep 17 00:00:00 2001 From: rocky Date: Mon, 15 Jun 2026 20:14:41 -0400 Subject: [PATCH 06/11] Rename extension .m to .wl ... Eventually, though, we'll remove all of these autoloaded files. --- .../autoload/formats/Base64/{Import.m => Import.wl} | 0 mathics/autoload/formats/CSV/{Import.m => Import.wl} | 0 mathics/autoload/formats/HTML/{Import.m => Import.wl} | 0 .../autoload/formats/Image/{Import.m => Import.wl} | 0 mathics/autoload/formats/JSON/{Import.m => Import.wl} | 0 .../autoload/formats/PACKAGE/{Import.m => Import.wl} | 0 mathics/autoload/formats/Text/{Import.m => Import.wl} | 0 mathics/autoload/formats/XML/{Import.m => Import.wl} | 0 mathics/autoload/formats/ZIP/Import.wl | 11 ----------- 9 files changed, 11 deletions(-) rename mathics/autoload/formats/Base64/{Import.m => Import.wl} (100%) rename mathics/autoload/formats/CSV/{Import.m => Import.wl} (100%) rename mathics/autoload/formats/HTML/{Import.m => Import.wl} (100%) rename mathics/autoload/formats/Image/{Import.m => Import.wl} (100%) rename mathics/autoload/formats/JSON/{Import.m => Import.wl} (100%) rename mathics/autoload/formats/PACKAGE/{Import.m => Import.wl} (100%) rename mathics/autoload/formats/Text/{Import.m => Import.wl} (100%) rename mathics/autoload/formats/XML/{Import.m => Import.wl} (100%) delete mode 100644 mathics/autoload/formats/ZIP/Import.wl diff --git a/mathics/autoload/formats/Base64/Import.m b/mathics/autoload/formats/Base64/Import.wl similarity index 100% rename from mathics/autoload/formats/Base64/Import.m rename to mathics/autoload/formats/Base64/Import.wl diff --git a/mathics/autoload/formats/CSV/Import.m b/mathics/autoload/formats/CSV/Import.wl similarity index 100% rename from mathics/autoload/formats/CSV/Import.m rename to mathics/autoload/formats/CSV/Import.wl diff --git a/mathics/autoload/formats/HTML/Import.m b/mathics/autoload/formats/HTML/Import.wl similarity index 100% rename from mathics/autoload/formats/HTML/Import.m rename to mathics/autoload/formats/HTML/Import.wl diff --git a/mathics/autoload/formats/Image/Import.m b/mathics/autoload/formats/Image/Import.wl similarity index 100% rename from mathics/autoload/formats/Image/Import.m rename to mathics/autoload/formats/Image/Import.wl diff --git a/mathics/autoload/formats/JSON/Import.m b/mathics/autoload/formats/JSON/Import.wl similarity index 100% rename from mathics/autoload/formats/JSON/Import.m rename to mathics/autoload/formats/JSON/Import.wl diff --git a/mathics/autoload/formats/PACKAGE/Import.m b/mathics/autoload/formats/PACKAGE/Import.wl similarity index 100% rename from mathics/autoload/formats/PACKAGE/Import.m rename to mathics/autoload/formats/PACKAGE/Import.wl diff --git a/mathics/autoload/formats/Text/Import.m b/mathics/autoload/formats/Text/Import.wl similarity index 100% rename from mathics/autoload/formats/Text/Import.m rename to mathics/autoload/formats/Text/Import.wl diff --git a/mathics/autoload/formats/XML/Import.m b/mathics/autoload/formats/XML/Import.wl similarity index 100% rename from mathics/autoload/formats/XML/Import.m rename to mathics/autoload/formats/XML/Import.wl diff --git a/mathics/autoload/formats/ZIP/Import.wl b/mathics/autoload/formats/ZIP/Import.wl deleted file mode 100644 index 7a7fa98ec..000000000 --- a/mathics/autoload/formats/ZIP/Import.wl +++ /dev/null @@ -1,11 +0,0 @@ -(* ZIP compressed file and file archive Importer *) - -Begin["System`ZIP`"] -ImportExport`RegisterImport[ - "ZIP", - ImportZip, - AvailableElements -> {"FileNames", "Summary"}, - DefaultElement -> "FileNames" -] - -End[] From 19be2606b2923204f857a729e249dde9ba4e7b77 Mon Sep 17 00:00:00 2001 From: rocky Date: Mon, 15 Jun 2026 20:30:49 -0400 Subject: [PATCH 07/11] AvailableElements -> Elements And isolate Import[xxx, Elements] tests --- mathics/autoload/formats/Base64/Import.wl | 2 +- mathics/autoload/formats/CSV/Import.wl | 2 +- mathics/autoload/formats/HTML/Import.wl | 4 +- mathics/autoload/formats/Image/Import.wl | 2 +- mathics/autoload/formats/Text/Import.wl | 2 +- mathics/autoload/formats/XML/Import.wl | 4 +- mathics/builtin/files_io/importexport.py | 4 +- mathics/eval/files_io/importexport.py | 2 +- test/builtin/files_io/test_importexport.py | 50 ++++++++++++++-------- 9 files changed, 42 insertions(+), 30 deletions(-) diff --git a/mathics/autoload/formats/Base64/Import.wl b/mathics/autoload/formats/Base64/Import.wl index c99f2286c..00db22da0 100644 --- a/mathics/autoload/formats/Base64/Import.wl +++ b/mathics/autoload/formats/Base64/Import.wl @@ -19,7 +19,7 @@ ImportExport`RegisterImport[ B64Import, (* Sources -> ImportExport`DefaultSources["Table"], *) FunctionChannels -> {"FileNames"}, - AvailableElements -> {"Data"}, + Elements -> {"Data"}, DefaultElement -> "Data", Options -> { "CharacterEncoding" diff --git a/mathics/autoload/formats/CSV/Import.wl b/mathics/autoload/formats/CSV/Import.wl index 945865d2a..16bd3bb1a 100644 --- a/mathics/autoload/formats/CSV/Import.wl +++ b/mathics/autoload/formats/CSV/Import.wl @@ -27,7 +27,7 @@ ImportExport`RegisterImport[ }, (* Sources -> ImportExport`DefaultSources["Table"], *) FunctionChannels -> {"Streams"}, - AvailableElements -> {"Data", "Grid"}, + Elements -> {"Data", "Grid"}, DefaultElement -> "Data", Options -> { "CharacterEncoding", diff --git a/mathics/autoload/formats/HTML/Import.wl b/mathics/autoload/formats/HTML/Import.wl index a82058487..9294c457c 100644 --- a/mathics/autoload/formats/HTML/Import.wl +++ b/mathics/autoload/formats/HTML/Import.wl @@ -16,8 +16,8 @@ ImportExport`RegisterImport[ HTML`PlaintextImport }, {}, - AvailableElements -> {"Data", "FullData", "Hyperlinks", "ImageLinks", "Plaintext", "Source", "Title", "XMLObject"}, - DefaultElement -> "Plaintext", + Elements -> {"Data", "FullData", "Hyperlinks", "ImageLinks", "Plaintext", "Source", "Title", "XMLObject"}, + DefaultElement -> "Plaintext", FunctionChannels -> {"FileNames"} ] diff --git a/mathics/autoload/formats/Image/Import.wl b/mathics/autoload/formats/Image/Import.wl index d1e0477a6..01bbad677 100644 --- a/mathics/autoload/formats/Image/Import.wl +++ b/mathics/autoload/formats/Image/Import.wl @@ -7,7 +7,7 @@ RegisterImageImport[type_] := ImportExport`RegisterImport[ type, System`ImageImport, {}, - AvailableElements -> {"Image"}, + Elements -> {"Image"}, DefaultElement -> "Image", FunctionChannels -> {"FileNames"} ]; diff --git a/mathics/autoload/formats/Text/Import.wl b/mathics/autoload/formats/Text/Import.wl index e4f6d6158..91fe885f9 100644 --- a/mathics/autoload/formats/Text/Import.wl +++ b/mathics/autoload/formats/Text/Import.wl @@ -44,7 +44,7 @@ ImportExport`RegisterImport[ System`Convert`TextDump`PlaintextImport }, {}, - AvailableElements -> {"Data", "Lines", "Plaintext", "String", "Words"}, + Elements -> {"Data", "Lines", "Plaintext", "String", "Words"}, BinaryFormat -> True, DefaultElement -> "Plaintext", FunctionChannels -> {"Streams"}, diff --git a/mathics/autoload/formats/XML/Import.wl b/mathics/autoload/formats/XML/Import.wl index cbe76f164..ecfdbcf0f 100644 --- a/mathics/autoload/formats/XML/Import.wl +++ b/mathics/autoload/formats/XML/Import.wl @@ -13,8 +13,8 @@ ImportExport`RegisterImport[ XML`XMLObjectImport }, {}, - AvailableElements -> {"Plaintext", "Tags", "XMLObject"}, - DefaultElement -> "XMLObject", + Elements -> {"Plaintext", "Tags", "XMLObject"}, + DefaultElement -> "XMLObject", FunctionChannels -> {"FileNames"} ] diff --git a/mathics/builtin/files_io/importexport.py b/mathics/builtin/files_io/importexport.py index b3ce3b14d..d909cef40 100644 --- a/mathics/builtin/files_io/importexport.py +++ b/mathics/builtin/files_io/importexport.py @@ -963,7 +963,7 @@ class RegisterImport(Builtin): # XXX OptionsIssue options = { "AlphaChannel": "False", - "AvailableElements": "None", + "Elements": "None", "BinaryFormat": "False", "DefaultElement": "Automatic", "Encoding": "False", @@ -1056,7 +1056,7 @@ class RegisterExport(Builtin): options = { "AlphaChannel": "False", - "AvailableElements": "None", + "Elements": "None", "BinaryFormat": "False", "DefaultElement": "None", "Encoding": "False", diff --git a/mathics/eval/files_io/importexport.py b/mathics/eval/files_io/importexport.py index 4a0e15b9a..8abd32492 100644 --- a/mathics/eval/files_io/importexport.py +++ b/mathics/eval/files_io/importexport.py @@ -407,7 +407,7 @@ def eval_Import_Elements(file_format: str, evaluation): # 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") + return options.get("System`Elements") # FIXME: diff --git a/test/builtin/files_io/test_importexport.py b/test/builtin/files_io/test_importexport.py index 10cd3183e..fa5a93a2f 100644 --- a/test/builtin/files_io/test_importexport.py +++ b/test/builtin/files_io/test_importexport.py @@ -105,6 +105,37 @@ def test_export(): """ +@pytest.mark.parametrize( + ("str_expr", "msgs", "str_expected", "fail_msg"), + [ + ( + 'Import["ExampleData/numberdata.csv", "Elements"]', + None, + "{Data, Grid}", + None, + ), + ( + 'datastring = "0.88, 0.60, 0.94\\n.076, 0.19, .51\\n0.97, 0.04, .26";ImportString[datastring, "Elements"]', + None, + "{Data, Lines, Plaintext, String, Words}", + None, + ), + ('ImportString[datastring, {"CSV","Elements"}]', None, "{Data, Grid}", None), + ], +) +def test_import_elements(str_expr, msgs, str_expected, fail_msg): + """ """ + check_evaluation( + str_expr, + str_expected, + to_string_expr=True, + to_string_expected=True, + hold_expected=True, + failure_message=fail_msg, + expected_messages=msgs, + ) + + @pytest.mark.parametrize( ("str_expr", "msgs", "str_expected", "fail_msg"), [ @@ -124,18 +155,6 @@ def test_export(): None, ), ## CSV - ( - 'Import["ExampleData/numberdata.csv", "Elements"]', - None, - "{Data, Grid}", - None, - ), - ( - 'Import["ExampleData/numberdata.csv", "Data"]', - None, - "{{0.88, 0.60, 0.94}, {0.76, 0.19, 0.51}, {0.97, 0.04, 0.26}, {0.33, 0.74, 0.79}, {0.42, 0.64, 0.56}}", - None, - ), ( 'Import["ExampleData/numberdata.csv"]', None, @@ -188,13 +207,6 @@ def test_export(): ), ("ImportString[x]", ("First argument x is not a string.",), "$Failed", None), ## CSV - ( - 'datastring = "0.88, 0.60, 0.94\\n.076, 0.19, .51\\n0.97, 0.04, .26";ImportString[datastring, "Elements"]', - None, - "{Data, Lines, Plaintext, String, Words}", - None, - ), - ('ImportString[datastring, {"CSV","Elements"}]', None, "{Data, Grid}", None), ( 'ImportString[datastring, {"CSV", "Data"}]', None, From bb872b2c48975522c14e1b7b62214b5836bd6242 Mon Sep 17 00:00:00 2001 From: rocky Date: Tue, 16 Jun 2026 07:19:40 -0400 Subject: [PATCH 08/11] Start Zip import --- mathics/autoload/formats/ZIP/Import.wl | 12 ++++++++++++ mathics/eval/files_io/importexport.py | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 mathics/autoload/formats/ZIP/Import.wl diff --git a/mathics/autoload/formats/ZIP/Import.wl b/mathics/autoload/formats/ZIP/Import.wl new file mode 100644 index 000000000..00318ce22 --- /dev/null +++ b/mathics/autoload/formats/ZIP/Import.wl @@ -0,0 +1,12 @@ +(* ZIP compressed file and file archive Importer *) + +Begin["System`ZIP`"] +ImportExport`RegisterImport[ + "ZIP", + ImportZip, + FunctionChannels -> {"Streams"}, + Elements -> {"FileNames", "Summary"}, + DefaultElement -> "FileNames" +] + +End[] diff --git a/mathics/eval/files_io/importexport.py b/mathics/eval/files_io/importexport.py index 8abd32492..57427928a 100644 --- a/mathics/eval/files_io/importexport.py +++ b/mathics/eval/files_io/importexport.py @@ -436,9 +436,9 @@ def eval_JSONImport(json_path: str) -> ListExpression: # FIXME: # We should not be extracting everything and returning a list of rules. # provide a better interface. -def eval_ZIPImport(zip_path: String) -> ListExpression: +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.value, "r") as archive: + 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) From 5c4de0a5de35fb130cc8b5068048bdc2302fcc9a Mon Sep 17 00:00:00 2001 From: rocky Date: Tue, 16 Jun 2026 07:22:35 -0400 Subject: [PATCH 09/11] Revert Elements->AvailableElements AvailableElements is what WMA uses. --- mathics/autoload/formats/Base64/Import.wl | 2 +- mathics/autoload/formats/CSV/Import.wl | 2 +- mathics/autoload/formats/HTML/Import.wl | 4 +- mathics/autoload/formats/Image/Import.wl | 2 +- mathics/autoload/formats/Text/Import.wl | 2 +- mathics/autoload/formats/XML/Import.wl | 4 +- mathics/autoload/formats/ZIP/Import.wl | 2 +- mathics/builtin/files_io/importexport.py | 4 +- mathics/eval/files_io/importexport.py | 2 +- test/builtin/files_io/test_importexport.py | 50 ++++++++-------------- 10 files changed, 31 insertions(+), 43 deletions(-) diff --git a/mathics/autoload/formats/Base64/Import.wl b/mathics/autoload/formats/Base64/Import.wl index 00db22da0..c99f2286c 100644 --- a/mathics/autoload/formats/Base64/Import.wl +++ b/mathics/autoload/formats/Base64/Import.wl @@ -19,7 +19,7 @@ ImportExport`RegisterImport[ B64Import, (* Sources -> ImportExport`DefaultSources["Table"], *) FunctionChannels -> {"FileNames"}, - Elements -> {"Data"}, + AvailableElements -> {"Data"}, DefaultElement -> "Data", Options -> { "CharacterEncoding" diff --git a/mathics/autoload/formats/CSV/Import.wl b/mathics/autoload/formats/CSV/Import.wl index 16bd3bb1a..945865d2a 100644 --- a/mathics/autoload/formats/CSV/Import.wl +++ b/mathics/autoload/formats/CSV/Import.wl @@ -27,7 +27,7 @@ ImportExport`RegisterImport[ }, (* Sources -> ImportExport`DefaultSources["Table"], *) FunctionChannels -> {"Streams"}, - Elements -> {"Data", "Grid"}, + AvailableElements -> {"Data", "Grid"}, DefaultElement -> "Data", Options -> { "CharacterEncoding", diff --git a/mathics/autoload/formats/HTML/Import.wl b/mathics/autoload/formats/HTML/Import.wl index 9294c457c..a82058487 100644 --- a/mathics/autoload/formats/HTML/Import.wl +++ b/mathics/autoload/formats/HTML/Import.wl @@ -16,8 +16,8 @@ ImportExport`RegisterImport[ HTML`PlaintextImport }, {}, - Elements -> {"Data", "FullData", "Hyperlinks", "ImageLinks", "Plaintext", "Source", "Title", "XMLObject"}, - DefaultElement -> "Plaintext", + AvailableElements -> {"Data", "FullData", "Hyperlinks", "ImageLinks", "Plaintext", "Source", "Title", "XMLObject"}, + DefaultElement -> "Plaintext", FunctionChannels -> {"FileNames"} ] diff --git a/mathics/autoload/formats/Image/Import.wl b/mathics/autoload/formats/Image/Import.wl index 01bbad677..d1e0477a6 100644 --- a/mathics/autoload/formats/Image/Import.wl +++ b/mathics/autoload/formats/Image/Import.wl @@ -7,7 +7,7 @@ RegisterImageImport[type_] := ImportExport`RegisterImport[ type, System`ImageImport, {}, - Elements -> {"Image"}, + AvailableElements -> {"Image"}, DefaultElement -> "Image", FunctionChannels -> {"FileNames"} ]; diff --git a/mathics/autoload/formats/Text/Import.wl b/mathics/autoload/formats/Text/Import.wl index 91fe885f9..e4f6d6158 100644 --- a/mathics/autoload/formats/Text/Import.wl +++ b/mathics/autoload/formats/Text/Import.wl @@ -44,7 +44,7 @@ ImportExport`RegisterImport[ System`Convert`TextDump`PlaintextImport }, {}, - Elements -> {"Data", "Lines", "Plaintext", "String", "Words"}, + AvailableElements -> {"Data", "Lines", "Plaintext", "String", "Words"}, BinaryFormat -> True, DefaultElement -> "Plaintext", FunctionChannels -> {"Streams"}, diff --git a/mathics/autoload/formats/XML/Import.wl b/mathics/autoload/formats/XML/Import.wl index ecfdbcf0f..cbe76f164 100644 --- a/mathics/autoload/formats/XML/Import.wl +++ b/mathics/autoload/formats/XML/Import.wl @@ -13,8 +13,8 @@ ImportExport`RegisterImport[ XML`XMLObjectImport }, {}, - Elements -> {"Plaintext", "Tags", "XMLObject"}, - DefaultElement -> "XMLObject", + AvailableElements -> {"Plaintext", "Tags", "XMLObject"}, + DefaultElement -> "XMLObject", FunctionChannels -> {"FileNames"} ] diff --git a/mathics/autoload/formats/ZIP/Import.wl b/mathics/autoload/formats/ZIP/Import.wl index 00318ce22..b3bce484d 100644 --- a/mathics/autoload/formats/ZIP/Import.wl +++ b/mathics/autoload/formats/ZIP/Import.wl @@ -5,7 +5,7 @@ ImportExport`RegisterImport[ "ZIP", ImportZip, FunctionChannels -> {"Streams"}, - Elements -> {"FileNames", "Summary"}, + AvailableElements -> {"FileNames", "Summary"}, DefaultElement -> "FileNames" ] diff --git a/mathics/builtin/files_io/importexport.py b/mathics/builtin/files_io/importexport.py index d909cef40..b3ce3b14d 100644 --- a/mathics/builtin/files_io/importexport.py +++ b/mathics/builtin/files_io/importexport.py @@ -963,7 +963,7 @@ class RegisterImport(Builtin): # XXX OptionsIssue options = { "AlphaChannel": "False", - "Elements": "None", + "AvailableElements": "None", "BinaryFormat": "False", "DefaultElement": "Automatic", "Encoding": "False", @@ -1056,7 +1056,7 @@ class RegisterExport(Builtin): options = { "AlphaChannel": "False", - "Elements": "None", + "AvailableElements": "None", "BinaryFormat": "False", "DefaultElement": "None", "Encoding": "False", diff --git a/mathics/eval/files_io/importexport.py b/mathics/eval/files_io/importexport.py index 57427928a..c409acda1 100644 --- a/mathics/eval/files_io/importexport.py +++ b/mathics/eval/files_io/importexport.py @@ -407,7 +407,7 @@ def eval_Import_Elements(file_format: str, evaluation): # In this we've registered, the field names that can be asked for # under the option "Elements". _, _, _, options = IMPORTERS[filetype] - return options.get("System`Elements") + return options.get("System`AvailableElements") # FIXME: diff --git a/test/builtin/files_io/test_importexport.py b/test/builtin/files_io/test_importexport.py index fa5a93a2f..10cd3183e 100644 --- a/test/builtin/files_io/test_importexport.py +++ b/test/builtin/files_io/test_importexport.py @@ -105,37 +105,6 @@ def test_export(): """ -@pytest.mark.parametrize( - ("str_expr", "msgs", "str_expected", "fail_msg"), - [ - ( - 'Import["ExampleData/numberdata.csv", "Elements"]', - None, - "{Data, Grid}", - None, - ), - ( - 'datastring = "0.88, 0.60, 0.94\\n.076, 0.19, .51\\n0.97, 0.04, .26";ImportString[datastring, "Elements"]', - None, - "{Data, Lines, Plaintext, String, Words}", - None, - ), - ('ImportString[datastring, {"CSV","Elements"}]', None, "{Data, Grid}", None), - ], -) -def test_import_elements(str_expr, msgs, str_expected, fail_msg): - """ """ - check_evaluation( - str_expr, - str_expected, - to_string_expr=True, - to_string_expected=True, - hold_expected=True, - failure_message=fail_msg, - expected_messages=msgs, - ) - - @pytest.mark.parametrize( ("str_expr", "msgs", "str_expected", "fail_msg"), [ @@ -155,6 +124,18 @@ def test_import_elements(str_expr, msgs, str_expected, fail_msg): None, ), ## CSV + ( + 'Import["ExampleData/numberdata.csv", "Elements"]', + None, + "{Data, Grid}", + None, + ), + ( + 'Import["ExampleData/numberdata.csv", "Data"]', + None, + "{{0.88, 0.60, 0.94}, {0.76, 0.19, 0.51}, {0.97, 0.04, 0.26}, {0.33, 0.74, 0.79}, {0.42, 0.64, 0.56}}", + None, + ), ( 'Import["ExampleData/numberdata.csv"]', None, @@ -207,6 +188,13 @@ def test_import_elements(str_expr, msgs, str_expected, fail_msg): ), ("ImportString[x]", ("First argument x is not a string.",), "$Failed", None), ## CSV + ( + 'datastring = "0.88, 0.60, 0.94\\n.076, 0.19, .51\\n0.97, 0.04, .26";ImportString[datastring, "Elements"]', + None, + "{Data, Lines, Plaintext, String, Words}", + None, + ), + ('ImportString[datastring, {"CSV","Elements"}]', None, "{Data, Grid}", None), ( 'ImportString[datastring, {"CSV", "Data"}]', None, From 98c9aeae77291ffbe695042285f7546d9ed083af Mon Sep 17 00:00:00 2001 From: rocky Date: Tue, 16 Jun 2026 09:19:06 -0400 Subject: [PATCH 10/11] Move Formats to SystemFiles This is where it is in WMA. Also, some administriva. File locations have been adjusted. --- MANIFEST.in | 2 +- README.rst | 4 ++-- .../{autoload/formats => SystemFiles/Formats}/Asy/Export.m | 0 .../Export.m => SystemFiles/Formats/Base64/Export.wl} | 2 ++ .../formats => SystemFiles/Formats}/Base64/Import.wl | 3 +++ .../CSV/Export.m => SystemFiles/Formats/CSV/Export.wl} | 6 ++++-- .../{autoload/formats => SystemFiles/Formats}/CSV/Import.wl | 3 +++ .../formats => SystemFiles/Formats}/HTML/Import.wl | 0 .../formats => SystemFiles/Formats}/Image/Export.m | 0 .../formats => SystemFiles/Formats}/Image/Import.wl | 0 .../formats => SystemFiles/Formats}/JSON/Import.wl | 0 .../formats => SystemFiles/Formats}/PACKAGE/Import.wl | 0 .../SVG/Export.m => SystemFiles/Formats/SVG/Export.wl} | 4 ++-- .../{autoload/formats => SystemFiles/Formats}/Text/Export.m | 0 .../formats => SystemFiles/Formats}/Text/Import.wl | 0 .../{autoload/formats => SystemFiles/Formats}/XML/Import.wl | 0 .../{autoload/formats => SystemFiles/Formats}/ZIP/Import.wl | 0 mathics/builtin/files_io/importexport.py | 2 +- mathics/core/definitions.py | 1 + pyproject.toml | 6 ++---- 20 files changed, 21 insertions(+), 12 deletions(-) rename mathics/{autoload/formats => SystemFiles/Formats}/Asy/Export.m (100%) rename mathics/{autoload/formats/Base64/Export.m => SystemFiles/Formats/Base64/Export.wl} (94%) rename mathics/{autoload/formats => SystemFiles/Formats}/Base64/Import.wl (94%) rename mathics/{autoload/formats/CSV/Export.m => SystemFiles/Formats/CSV/Export.wl} (94%) rename mathics/{autoload/formats => SystemFiles/Formats}/CSV/Import.wl (94%) rename mathics/{autoload/formats => SystemFiles/Formats}/HTML/Import.wl (100%) rename mathics/{autoload/formats => SystemFiles/Formats}/Image/Export.m (100%) rename mathics/{autoload/formats => SystemFiles/Formats}/Image/Import.wl (100%) rename mathics/{autoload/formats => SystemFiles/Formats}/JSON/Import.wl (100%) rename mathics/{autoload/formats => SystemFiles/Formats}/PACKAGE/Import.wl (100%) rename mathics/{autoload/formats/SVG/Export.m => SystemFiles/Formats/SVG/Export.wl} (91%) rename mathics/{autoload/formats => SystemFiles/Formats}/Text/Export.m (100%) rename mathics/{autoload/formats => SystemFiles/Formats}/Text/Import.wl (100%) rename mathics/{autoload/formats => SystemFiles/Formats}/XML/Import.wl (100%) rename mathics/{autoload/formats => SystemFiles/Formats}/ZIP/Import.wl (100%) 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.wl b/mathics/SystemFiles/Formats/Base64/Import.wl similarity index 94% rename from mathics/autoload/formats/Base64/Import.wl rename to mathics/SystemFiles/Formats/Base64/Import.wl index c99f2286c..b1cb12a1d 100644 --- a/mathics/autoload/formats/Base64/Import.wl +++ b/mathics/SystemFiles/Formats/Base64/Import.wl @@ -1,3 +1,5 @@ +(* ::Package:: *) + Begin["System`Convert`B64Dump`"] @@ -21,6 +23,7 @@ ImportExport`RegisterImport[ 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.wl b/mathics/SystemFiles/Formats/CSV/Import.wl similarity index 94% rename from mathics/autoload/formats/CSV/Import.wl rename to mathics/SystemFiles/Formats/CSV/Import.wl index 945865d2a..cf5f6e646 100644 --- a/mathics/autoload/formats/CSV/Import.wl +++ b/mathics/SystemFiles/Formats/CSV/Import.wl @@ -1,3 +1,5 @@ +(* ::Package:: *) + (* CSV Importer *) Begin["System`Convert`TableDump`"] @@ -29,6 +31,7 @@ ImportExport`RegisterImport[ FunctionChannels -> {"Streams"}, AvailableElements -> {"Data", "Grid"}, DefaultElement -> "Data", + BinaryFormat -> False, Options -> { "CharacterEncoding", "FieldSeparators" diff --git a/mathics/autoload/formats/HTML/Import.wl b/mathics/SystemFiles/Formats/HTML/Import.wl similarity index 100% rename from mathics/autoload/formats/HTML/Import.wl 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.wl b/mathics/SystemFiles/Formats/Image/Import.wl similarity index 100% rename from mathics/autoload/formats/Image/Import.wl rename to mathics/SystemFiles/Formats/Image/Import.wl diff --git a/mathics/autoload/formats/JSON/Import.wl b/mathics/SystemFiles/Formats/JSON/Import.wl similarity index 100% rename from mathics/autoload/formats/JSON/Import.wl rename to mathics/SystemFiles/Formats/JSON/Import.wl diff --git a/mathics/autoload/formats/PACKAGE/Import.wl b/mathics/SystemFiles/Formats/PACKAGE/Import.wl similarity index 100% rename from mathics/autoload/formats/PACKAGE/Import.wl 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.wl b/mathics/SystemFiles/Formats/Text/Import.wl similarity index 100% rename from mathics/autoload/formats/Text/Import.wl rename to mathics/SystemFiles/Formats/Text/Import.wl diff --git a/mathics/autoload/formats/XML/Import.wl b/mathics/SystemFiles/Formats/XML/Import.wl similarity index 100% rename from mathics/autoload/formats/XML/Import.wl rename to mathics/SystemFiles/Formats/XML/Import.wl diff --git a/mathics/autoload/formats/ZIP/Import.wl b/mathics/SystemFiles/Formats/ZIP/Import.wl similarity index 100% rename from mathics/autoload/formats/ZIP/Import.wl rename to mathics/SystemFiles/Formats/ZIP/Import.wl diff --git a/mathics/builtin/files_io/importexport.py b/mathics/builtin/files_io/importexport.py index b3ce3b14d..694aae135 100644 --- a/mathics/builtin/files_io/importexport.py +++ b/mathics/builtin/files_io/importexport.py @@ -876,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$] 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/pyproject.toml b/pyproject.toml index 0e0257dca..9921f95f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,13 +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/*/Export.wl", - "autoload/formats/*/Import.m", - "autoload/formats/*/Import.wl", "data/*.csv", "data/*.json", "data/*.pcl", From 9670a25d853d3accfa6cc32c55dea43e6eff1fff Mon Sep 17 00:00:00 2001 From: rocky Date: Tue, 16 Jun 2026 14:20:57 -0400 Subject: [PATCH 11/11] Correct elements pattern --- mathics/builtin/files_io/importexport.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mathics/builtin/files_io/importexport.py b/mathics/builtin/files_io/importexport.py index 694aae135..8f03c2eef 100644 --- a/mathics/builtin/files_io/importexport.py +++ b/mathics/builtin/files_io/importexport.py @@ -1279,7 +1279,7 @@ def eval(self, path: String, evaluation: Evaluation): return eval_JSONImport(path.value) def eval_elements(self, path: String, evaluation: Evaluation): - """%(name)s[path_String]["Elements"]""" + """%(name)s[path_String, "Elements"]""" return eval_JSONImport(path.value) @@ -1304,8 +1304,8 @@ def eval(self, path: String, evaluation: Evaluation): return eval_ZIPImport(path.value) def eval_elements(self, path: String, evaluation: Evaluation): - """%(name)s[path_String]["Elements"]""" - return eval_ZIPImport(path.value) + """%(name)s[path_String, "Elements"]""" + return eval_Import_Elements("ZIP", evaluation) class ImportString(Builtin):