diff --git a/python/install_pytrexio.sh b/python/install_pytrexio.sh index a880193..764bc85 100755 --- a/python/install_pytrexio.sh +++ b/python/install_pytrexio.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash set -x set -e @@ -38,12 +38,12 @@ python3 -m pip install -r requirements.txt # Create build directory and compile extension files (*.c) # --no-user-cfg disables custom .cfg files of the user machine, so that only setup.cfg is used -#python3 -s setup.py --no-user-cfg build +#python3 -s setup.py --no-user-cfg build # Local inplace build of the .so module with SWIG-produced pytrexio_wrap.c (from the SWIG documentation) #python3 setup.py build_ext --inplace --swig-opts="-modern" -# Create distributions: +# Create distributions: # OLD WAY (DEPRECATED BY PYPA) # 1) sdist produces .tar.gz with all files necessary for manual compilation; @@ -54,14 +54,7 @@ python3 -m pip install -r requirements.txt python3 -m build --sdist --wheel --outdir dist/ # Install pytrexio in the current environment from the aforementioned wheel - -# OLD WAY -# --force-reinstall is needed here because build-system pre-installs pytrexio in the environment -# but does not install things in the corresponding site-packages directory -#python3 -m pip install dist/trexio-*.whl --force-reinstall - -# NEW WAY -python3 -m pip install dist/trexio-*.whl +python3 -m pip install dist/trexio-*.whl --force-reinstall # Run the command below in the root directory to install the package in 'editable' (-e) mode without dependencies (--no-deps) #python -m pip install -e . --no-deps @@ -83,7 +76,6 @@ rm -rf build dist trexio.egg-info # Additional information related to the installation of the TREXIO Python API -#Removing MANIFEST.in leads to issues in the installation. In particular, the .c and .h source files do not get copied -#from the src/ directory into the tar.gz which is produced by setup sdist command. +#Removing MANIFEST.in leads to issues in the installation. In particular, the .c and .h source files do not get copied +#from the src/ directory into the tar.gz which is produced by setup sdist command. #These source files are required to build the pytrexio.so extension module, which is needed for the Python API. - diff --git a/python/test/test_api.py b/python/test/test_api.py index b1ea2df..6859f7b 100644 --- a/python/test/test_api.py +++ b/python/test/test_api.py @@ -40,6 +40,7 @@ except: # create TREXIO file and open it for writing test_file = trexio.File(output_filename, mode='w', back_end=TEST_TREXIO_BACKEND) +assert test_file.exists # Print docstring of the trexio.open function #print(trexio.open.__doc__) @@ -153,6 +154,7 @@ del test_file # open previously created TREXIO file, now in 'read' mode test_file2 = trexio.File(output_filename, 'r', TEST_TREXIO_BACKEND) +assert test_file2.exists # check for existence of some of the previously written variables assert trexio.has_nucleus_num diff --git a/src/templates_front/templator_front.org b/src/templates_front/templator_front.org index 4f06b14..03936bf 100644 --- a/src/templates_front/templator_front.org +++ b/src/templates_front/templator_front.org @@ -1,5 +1,4 @@ #+TITLE: Front end API -break; #+PROPERTY: comments org #+SETUPFILE: ../../docs/theme.setup # -*- mode: org -*- @@ -696,12 +695,15 @@ class File: self.back_end = back_end self.isOpen = False + self.exists = False if pytrexio_s is None: - self.pytrexio_s = open(filename, mode, back_end) + self.pytrexio_s = _open(filename, mode, back_end) self.isOpen = True + self.exists = True else: self.pytrexio_s = pytrexio_s self.isOpen = None + self.exists = None self.info = info @@ -710,17 +712,23 @@ class File: """Close a TREXIO File.""" if self.isOpen: - close(self.pytrexio_s) + _close(self.pytrexio_s) self.isOpen = False else: raise Exception("TREXIO File object has not been opened.") + def inquire(self): + """Inquire whether a TREXIO file exists.""" + + self.exists = _inquire(self.filename) + + def __del__(self): """This is a TREXIO File destructor.""" if self.isOpen: - close(self.pytrexio_s) + _close(self.pytrexio_s) elif self.isOpen is None: raise Exception("[WIP]: TREXIO file handle provided but what if the file is already closed?") else: @@ -972,7 +980,7 @@ end interface *** Python #+begin_src python :tangle basic_python.py -def open(file_name: str, mode: str, back_end: int): +def _open(file_name: str, mode: str, back_end: int): """Create TREXIO file or open existing one. Parameters: @@ -1149,7 +1157,7 @@ end interface *** Python #+begin_src python :tangle basic_python.py -def close(trexio_file): +def _close(trexio_file): """Close TREXIO file. Parameter is a ~trexio_file~ object that has been created by a call to ~open~ function. @@ -1163,6 +1171,77 @@ def close(trexio_file): raise #+end_src +** File existence + + ~trexio_inquire~ check whether TREXIO file exists. + + input parameters: + ~file_name~ - string containing file name + + output: + ~trexio_exit_code~ exit code. + +*** C + + #+begin_src c :tangle prefix_front.h :exports none +trexio_exit_code trexio_inquire(const char* file_name); + #+end_src + + #+begin_src c :tangle prefix_front.c +trexio_exit_code +trexio_inquire (const char* file_name) +{ + + if (file_name == NULL || file_name[0] == '\0') return TREXIO_INVALID_ARG_1; + + /* First check if the TREXIO file exists and if it is a directory */ + trexio_exit_code rc_text = trexio_text_inquire(file_name); + +#ifdef HAVE_HDF5 + /* FILE_ERROR here means that the file exists but it is not a directory -> check with HDF5 */ + if (rc_text == TREXIO_FILE_ERROR) { + trexio_exit_code rc_hdf5 = trexio_hdf5_inquire(file_name); + return rc_hdf5; + /* If rc_text is TREXIO_SUCCESS -> file is a TREXIO directory; TREXIO_FAILURE -> file/directory does not exist */ + } else { + return rc_text; + } +#else + return rc_text; +#endif +} + #+end_src + +*** Fortran + + The function below is a C binding. + The front end Fortran function for ~trexio_inquire~ can be found in the ~Fortran helper/wrapper functions~. + + #+begin_src f90 :tangle prefix_fortran.f90 +interface + integer function trexio_inquire_c (filename) bind(C, name="trexio_inquire") + use, intrinsic :: iso_c_binding + import + character(kind=c_char), dimension(*) :: filename + end function trexio_inquire_c +end interface + #+end_src + +*** Python + + #+begin_src python :tangle basic_python.py +def _inquire(file_name: str) -> bool: + """Check whether ~file_name~ TREXIO file exists. + """ + rc = pytr.trexio_inquire(trexio_file_name) + if rc == TREXIO_SUCCESS: + return True + elif rc == TREXIO_FAILURE: + return False + else: + raise Error(rc) + #+end_src + * Templates for front end ** Description @@ -1769,7 +1848,7 @@ trexio_write_$group_dset$_64 (trexio_t* const file, const $group_dset_dtype_doub case TREXIO_HDF5: #ifdef HAVE_HDF5 - rc = trexio_hdf5_write_$group_dset$(file, + rc = trexio_hdf5_write_$group_dset$(file, (const $group_dset_dtype_double$*) $group_dset$_p, rank, dims); break; @@ -2629,7 +2708,7 @@ trexio_write_$group_dset$(trexio_t* const file, if (file->one_based) { uint64_t index_size = rank * buffer_size; - + int32_t* index_sparse_p = CALLOC(index_size, int32_t); if (index_sparse_p == NULL) return TREXIO_ALLOCATION_FAILED; @@ -3801,6 +3880,23 @@ contains end function trexio_open #+end_src + The function below adapts the original C-based ~trexio_inquire~ for Fortran. + This is needed due to the same reasons as for ~trexio_open~ function. + Note, that Fortran interface calls the main ~TREXIO~ API, which is written in C. + + #+begin_src f90 :tangle helper_fortran.f90 + integer function trexio_inquire (filename) + use, intrinsic :: iso_c_binding + implicit none + character(len=*), intent(in) :: filename + character(len=len_trim(filename)+1) :: filename_c + + filename_c = trim(filename) // c_null_char + trexio_inquire = trexio_inquire_c(filename_c) + end function trexio_inquire + + #+end_src + The subroutine below transforms an array of Fortran strings into one big string using ~TREXIO_DELIM~ symbol as a delimeter and adds ~NULL~ character in the end in order to properly pass the desired string to C API. This is needed due to the fact that strings in C are terminated by ~NULL~ character ~\0~. diff --git a/src/templates_hdf5/templator_hdf5.org b/src/templates_hdf5/templator_hdf5.org index debaa4c..26a2400 100644 --- a/src/templates_hdf5/templator_hdf5.org +++ b/src/templates_hdf5/templator_hdf5.org @@ -69,13 +69,29 @@ typedef struct trexio_hdf5_s { } trexio_hdf5_t; #+end_src +** Template for HDF5 init/deinit #+begin_src c :tangle struct_hdf5.h :exports none trexio_exit_code trexio_hdf5_init(trexio_t* const file); trexio_exit_code trexio_hdf5_deinit(trexio_t* const file); +trexio_exit_code trexio_hdf5_inquire(const char* file_name); #+end_src -** Template for HDF5 init/deinit + #+begin_src c :tangle basic_hdf5.c +trexio_exit_code +trexio_hdf5_inquire(const char* file_name) +{ + /* H5Fis_hdf5 determines whether file is in HDF5 format */ + htri_t rc = H5Fis_hdf5(file_name); + if (rc > 0 ) { + return TREXIO_SUCCESS; //exists and HDF5 + } else if (rc == 0) { + return TREXIO_FILE_ERROR; //exists but not HDF5 + } else { + return TREXIO_FAILURE; //does not exist or function fails + } +} + #+end_src #+begin_src c :tangle basic_hdf5.c trexio_exit_code @@ -134,7 +150,9 @@ trexio_hdf5_init (trexio_t* const file) return TREXIO_SUCCESS; } + #+end_src + #+begin_src c :tangle basic_hdf5.c trexio_exit_code trexio_hdf5_deinit (trexio_t* const file) { diff --git a/src/templates_text/templator_text.org b/src/templates_text/templator_text.org index a8647ea..1242087 100644 --- a/src/templates_text/templator_text.org +++ b/src/templates_text/templator_text.org @@ -105,6 +105,33 @@ typedef struct trexio_text_s { #+begin_src c :tangle basic_text.h :exports none trexio_exit_code trexio_text_init(trexio_t* const file); +trexio_exit_code trexio_text_inquire(const char* file_name); +trexio_exit_code trexio_text_deinit(trexio_t* const file); +trexio_exit_code trexio_text_lock(trexio_t* const file); +trexio_exit_code trexio_text_unlock(trexio_t* const file); + #+end_src + + #+begin_src c :tangle basic_text.c +trexio_exit_code +trexio_text_inquire (const char* file_name) +{ + /* Check if the file with "file_name" exists and that it is a directory */ + struct stat st; + + int rc = stat(file_name, &st); + + bool file_exists = rc == 0; + + if (file_exists) { + + bool is_a_directory = st.st_mode & S_IFDIR; + if (!is_a_directory) return TREXIO_FILE_ERROR; + + return TREXIO_SUCCESS; + } else { + return TREXIO_FAILURE; + } +} #+end_src #+begin_src c :tangle basic_text.c @@ -119,30 +146,19 @@ trexio_text_init (trexio_t* const file) /* Put all pointers to NULL but leave parent untouched */ memset(&(f->parent)+1,0,sizeof(trexio_text_t)-sizeof(trexio_t)); - /* If directory doesn't exist, create it in write mode */ - struct stat st; + /* Check if directory exists */ + trexio_exit_code rc; + rc = trexio_text_inquire(file->file_name); + /* TREXIO file exists but is not a directory */ + if (rc == TREXIO_FILE_ERROR) return rc; + /* If directory does not exist - create it in write mode */ + if (rc == TREXIO_FAILURE) { - int rc = stat(file->file_name, &st); + if (file->mode == 'r') return TREXIO_READONLY; - bool file_exists = rc == 0; + int rc_dir = mkdir(file->file_name, 0777); + if (rc_dir != 0) return TREXIO_ERRNO; - if (file_exists) { - - bool is_a_directory = st.st_mode & S_IFDIR; - if (!is_a_directory) { - return TREXIO_FILE_ERROR; - } - - } else { - - if (file->mode == 'r') { - return TREXIO_READONLY; - } - - rc = mkdir(file->file_name, 0777); - if (rc != 0) { - return TREXIO_ERRNO; - } } /* Create the lock file in the directory */ @@ -166,7 +182,7 @@ trexio_text_init (trexio_t* const file) if (errno == EACCES) { /* The directory is read-only and the lock file can't be written. Create a dummy temporary file for dummy locking. - */ + ,*/ char dirname[TREXIO_MAX_FILENAME_LENGTH] = "/tmp/trexio.XXXXXX"; if (mkdtemp(dirname) == NULL) return TREXIO_ERRNO; strncpy (file_name, dirname, TREXIO_MAX_FILENAME_LENGTH); @@ -184,10 +200,6 @@ trexio_text_init (trexio_t* const file) } #+end_src - #+begin_src c :tangle basic_text.h :exports none -trexio_exit_code trexio_text_lock(trexio_t* const file); - #+end_src - #+begin_src c :tangle basic_text.c trexio_exit_code trexio_text_lock(trexio_t* const file) { if (file == NULL) return TREXIO_INVALID_ARG_1; @@ -210,15 +222,6 @@ trexio_exit_code trexio_text_lock(trexio_t* const file) { } #+end_src - - #+begin_src c :tangle basic_text.h :exports none -trexio_exit_code trexio_text_deinit(trexio_t* const file); - #+end_src - - #+begin_src c :tangle basic_text.h :exports none -trexio_exit_code trexio_text_unlock(trexio_t* const file); - #+end_src - #+begin_src c :tangle basic_text.c trexio_exit_code trexio_text_unlock (trexio_t* const file) diff --git a/tests/open_hdf5.c b/tests/open_hdf5.c index 20bc339..7f4ec62 100644 --- a/tests/open_hdf5.c +++ b/tests/open_hdf5.c @@ -85,7 +85,7 @@ static int test_open_errors (const back_end_t backend) { fprintf(stderr, "%s \n", trexio_string_of_error(rc)); // open existing file with non-supported back end, should return TREXIO_INVALID_ARG_3 - file = trexio_open(TREXIO_VOID, 'w', 666, &rc); + file = trexio_open(TREXIO_FILE, 'w', 666, &rc); assert (file == NULL); assert (rc == TREXIO_INVALID_ARG_3); fprintf(stderr, "%s \n", trexio_string_of_error(rc)); @@ -96,6 +96,28 @@ static int test_open_errors (const back_end_t backend) { } +static int test_inquire (const back_end_t backend) { + +/* Try to call trexio_inquire function */ + + trexio_exit_code rc; + +/*================= START OF TEST ==================*/ + + // inquire non-existing file + rc = trexio_inquire(TREXIO_VOID); + assert (rc == TREXIO_FAILURE); + + // inquire existing file + rc = trexio_inquire(TREXIO_FILE); + assert (rc == TREXIO_SUCCESS); + +/*================= END OF TEST ==================*/ + + return 0; +} + + int main(void) { /*============== Test launcher ================*/ @@ -107,11 +129,10 @@ int main(void) { test_open_w (TREXIO_FILE, TEST_BACKEND); test_open_r (TREXIO_FILE, TEST_BACKEND); test_open_errors(TEST_BACKEND); + test_inquire (TEST_BACKEND); rc = system(RM_COMMAND); assert (rc == 0); return 0; } - - diff --git a/tests/open_text.c b/tests/open_text.c index 0aa904a..4ec7810 100644 --- a/tests/open_text.c +++ b/tests/open_text.c @@ -85,7 +85,7 @@ static int test_open_errors (const back_end_t backend) { fprintf(stderr, "%s \n", trexio_string_of_error(rc)); // open existing file with non-supported back end, should return TREXIO_INVALID_ARG_3 - file = trexio_open(TREXIO_VOID, 'w', 666, &rc); + file = trexio_open(TREXIO_FILE, 'w', 666, &rc); assert (file == NULL); assert (rc == TREXIO_INVALID_ARG_3); fprintf(stderr, "%s \n", trexio_string_of_error(rc)); @@ -96,6 +96,28 @@ static int test_open_errors (const back_end_t backend) { } +static int test_inquire (const back_end_t backend) { + +/* Try to call trexio_inquire function */ + + trexio_exit_code rc; + +/*================= START OF TEST ==================*/ + + // inquire non-existing file + rc = trexio_inquire(TREXIO_VOID); + assert (rc == TREXIO_FAILURE); + + // inquire existing file + rc = trexio_inquire(TREXIO_FILE); + assert (rc == TREXIO_SUCCESS); + +/*================= END OF TEST ==================*/ + + return 0; +} + + int main(void) { /*============== Test launcher ================*/ @@ -107,11 +129,10 @@ int main(void) { test_open_w (TREXIO_FILE, TEST_BACKEND); test_open_r (TREXIO_FILE, TEST_BACKEND); test_open_errors(TEST_BACKEND); + test_inquire (TEST_BACKEND); rc = system(RM_COMMAND); assert (rc == 0); return 0; } - - diff --git a/tests/test_f.f90 b/tests/test_f.f90 index dcadec8..f994b68 100644 --- a/tests/test_f.f90 +++ b/tests/test_f.f90 @@ -103,6 +103,9 @@ subroutine test_write(file_name, back_end) ! ================= START OF TEST ===================== ! + rc = trexio_inquire(file_name) + call trexio_assert(rc, TREXIO_FAILURE) + trex_file = trexio_open(file_name, 'w', back_end, rc) call trexio_assert(rc, TREXIO_SUCCESS) @@ -217,6 +220,9 @@ subroutine test_read(file_name, back_end) ! ================= START OF TEST ===================== ! + rc = trexio_inquire(file_name) + call trexio_assert(rc, TREXIO_SUCCESS) + trex_file = trexio_open(file_name, 'r', back_end, rc) call trexio_assert(rc, TREXIO_SUCCESS)