Skip to content
Snippets Groups Projects
Commit 3c4bb8cb authored by Niko Ehrenfeuchter's avatar Niko Ehrenfeuchter :keyboard:
Browse files

Initial commit

parents
No related branches found
No related tags found
No related merge requests found
# VS Code workspace files
*.code-workspace
# maven / release generated files
target/
pom.xml.releaseBackup
# bytecode created when importing directly in Jython:
*py.class
:snake::coffee::nut_and_bolt::wrench:
A collection of commonly used Python helper functions.
This diff is collapsed.
# A collection of Python helper functions. :snake::coffee::nut_and_bolt::wrench:
This package contains a diverse collection of Python functions dealing with
paths, I/O (file handles, ...), strings etc.
It is packaged by Maven to be ready for being used in [ImageJ2][imagej].
Developed and provided by the [Imaging Core Facility (IMCF)][imcf] of the
Biozentrum, University of Basel, Switzerland.
# Contents
:note: TODO!
# Example usage
:note: TODO!
[imcf]: https://www.biozentrum.unibas.ch/imcf
[imagej]: https://imagej.net
pom.xml 0 → 100644
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.scijava</groupId>
<artifactId>pom-scijava</artifactId>
<version>23.2.0</version>
<relativePath />
</parent>
<groupId>ch.unibas.biozentrum.imcf</groupId>
<artifactId>python-imcflibs</artifactId>
<version>0.0.31-SNAPSHOT</version>
<name>python-imcflibs</name>
<description>
A collection of Python helper functions.
</description>
<url>https://github.com/imcf/python-imcflibs/</url>
<inceptionYear>2013</inceptionYear>
<organization>
<name>University of Basel</name>
<url>http://www.unibas.ch/</url>
</organization>
<licenses>
<license>
<name>GNU General Public License (GPL) v3+</name>
<url>https://www.gnu.org/licenses/gpl.html</url>
<distribution>repo</distribution>
</license>
</licenses>
<build>
<!-- Tell maven where to find the files to be packaged. Since the code
to be packaged is not mavenized itself but this is rather re-packaging
of non-Java code (hence not following the project layout conventions) we
need to do this explicitly. -->
<!-- The following filtering rule allows to automatically have the
project version added to the Python package's `__init__.py` file by
placing the string '${project.version}' somewhere in that file. -->
<resources>
<resource>
<directory>src</directory>
<filtering>true</filtering>
<includes>
<include>**/__init__.py</include>
</includes>
</resource>
<resource>
<directory>src</directory>
<filtering>false</filtering>
<excludes>
<exclude>**/__init__.py</exclude>
</excludes>
</resource>
</resources>
</build>
<developers>
<!-- See https://imagej.net/Team -->
<developer>
<id>Ehrenfeu</id>
<name>Niko Ehrenfeuchter</name>
<url>https://imagej.net/User:Ehrenfeu</url>
<roles>
<role>founder</role>
<role>lead</role>
<role>developer</role>
<role>debugger</role>
<role>reviewer</role>
<role>support</role>
<role>maintainer</role>
</roles>
</developer>
</developers>
<contributors>
<!--
NB: Need at least one element to override the parent.
See: https://issues.apache.org/jira/browse/MNG-5220
-->
<contributor>
<name>None</name>
</contributor>
</contributors>
<mailingLists>
<mailingList>
<name>Image.sc Forum</name>
<archive>https://forum.image.sc/</archive>
</mailingList>
</mailingLists>
<scm>
<connection>scm:git:git://github.com/imcf/python-imcflibs</connection>
<developerConnection>scm:git:git@github.com:imcf/python-imcflibs</developerConnection>
<tag>HEAD</tag>
<url>https://github.com/imcf/python-imcflibs</url>
</scm>
<issueManagement>
<system>GitHub Issues</system>
<url>https://github.com/imcf/python-imcflibs/issues</url>
</issueManagement>
<ciManagement>
<system>None</system>
</ciManagement>
<properties>
<license.licenseName>gpl_v3</license.licenseName>
<license.copyrightOwners>University of Basel, Switzerland</license.copyrightOwners>
<maven.source.skip>true</maven.source.skip>
</properties>
<repositories>
<repository>
<id>imagej.public</id>
<url>http://maven.imagej.net/content/groups/public</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>net.imagej</groupId>
<artifactId>ij</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
#!/bin/bash
### exit on any error
set -e
cd $(dirname $0)/..
FIJI_APP="/opt/Fiji.app"
if [ -n "$1" ] ; then
FIJI_APP="$1"
fi
### build and deploy
mvn -Dimagej.app.directory="$FIJI_APP"
"""A collection of Python helper functions.
This package contains a diverse collection of Python functions dealing with
paths, I/O (file handles, ...), strings etc.
In addition (to the regular packaging) it is set up to be packaged by Maven to
be ready for being used in [ImageJ2][imagej].
Developed and provided by the [Imaging Core Facility (IMCF)][imcf] of the
Biozentrum, University of Basel, Switzerland.
# Contents
:note: TODO!
# Example usage
:note: TODO!
[imcf]: https://www.biozentrum.unibas.ch/imcf
[imagej]: https://imagej.net
"""
__version__ = '${project.version}'
"""I/O related functions."""
import zipfile
from os.path import splitext, join
from .log import LOG as log
from .strtools import flatten
def filehandle(fname, mode='r'):
"""Make sure a variable is either a filehandle or create one from it.
This function takes a variable and checks whether it is already a
filehandle with the desired mode or a string that can be turned into a
filehandle with that mode. This can be used e.g. to make functions agnostic
against being supplied a file-type parameter that was gathered via argparse
(then it's already a filehandle) or as a plain string.
Parameters
----------
fname : str or filehandle
mode : str
The desired mode of the filehandle (default=read).
Returns
-------
A valid (open) filehandle with the given mode. Raises an IOError
otherwise.
Example
-------
>>> fname = __file__
>>> type(fname)
<type 'str'>
>>> type(filehandle(fname))
<type 'file'>
>>> fh = open(__file__, 'r')
>>> type(fh)
<type 'file'>
>>> type(filehandle(fh))
<type 'file'>
"""
log.debug(type(fname))
if type(fname).__name__ == 'str':
try:
return open(fname, mode)
except IOError as err:
message = "can't open '%s': %s"
raise SystemExit(message % (fname, err))
elif type(fname).__name__ == 'file':
if fname.mode != mode:
message = "mode mismatch: %s != %s"
raise IOError(message % (fname.mode, mode))
return fname
else:
message = "unknown data type (expected string or filehandle): %s"
raise SystemExit(message % type(fname))
def readtxt(fname, path='', flat=False):
"""Commodity function for reading text files plain or zipped.
Read a text file line by line either plainly from a directory or a .zip or
.jar file. Return as a list of strings or optionally flattened into a
single string.
BEWARE: this is NOT intended for HUGE text files as it actually reads them
in and returns the content, not a handle to the reader itself!
Parameters
----------
fname : str
The name of the file to read in. Can be a full or relative path if
desired. For automatic archive handling use the 'path' parameter.
path : str (optional)
The directory where to look for the file. If the string has the suffix
'.zip' or '.jar' an archive is assumed and the corresponding mechanisms
are used to read 'fname' from within this archive.
flat : bool (optional)
Used to request a flattened string instead of a list of strings.
Returns
-------
txt : str or list(str)
Example
-------
>>> readtxt('foo', '/tmp/archive.zip', flat=True)
... # doctest: +SKIP
"""
zipread = None
suffix = splitext(path)[1].lower()
if ((suffix == '.zip') or (suffix == '.jar')):
# ZipFile only works as a context manager from Python 2.7 on
# tag:python25
zipread = zipfile.ZipFile(path, 'r')
fin = zipread.open(fname)
else:
fin = open(join(path, fname), 'r')
txt = fin.readlines() # returns file as a list, one entry per line
if flat:
txt = flatten(txt)
fin.close()
if zipread is not None:
zipread.close()
return txt
"""Common logging singleton for the package."""
import logging
LOG = logging.getLogger(__name__)
"""Helper functions to work with filenames."""
import platform
from os import sep
import os.path
def parse_path(path):
"""Parse a path into its components.
If the path doesn't end with the pathsep, it is assumed being a file!
No tests based on existing files are done, as this is supposed to also work
on path strings that don't exist on the system running this code.
Returns
-------
parsed = {
'orig' : str # string as passed into this function
'full' : str # separators adjusted to current platform
'path' : str # like previous, up to (including) the last separator
'dname' : str # segment between the last two separators (directory)
'fname' : str # segment after the last separator (filename)
'ext' : str # filename extension, containing max 1 period
}
Example
-------
>>> path_to_file = pathtools.parse_path('/tmp/foo/file')
>>> path_to_dir = pathtools.parse_path('/tmp/foo/')
orig : The full path string as given to this function.
full : Backslashes replaced by the current separator.
path : 'full' up to the last separator (included)
>>> path_to_file['path']
'/tmp/foo/'
>>> path_to_dir['path']
'/tmp/foo/'
dname : The last directory of the path in 'full'.
>>> path_to_file['dname']
'foo'
>>> path_to_dir['dname']
'foo'
fname : The filename of 'full', empty in case of a directory.
>>> path_to_file['fname']
'file'
>>> path_to_dir['fname']
''
"""
parsed = {}
parsed['orig'] = path
path = path.replace('\\', sep)
parsed['full'] = path
parsed['path'] = os.path.dirname(path) + sep
parsed['fname'] = os.path.basename(path)
parsed['dname'] = os.path.basename(os.path.dirname(parsed['path']))
parsed['ext'] = os.path.splitext(parsed['fname'])[1]
return parsed
def jython_fiji_exists(path):
"""Wrapper to work around problems with Jython 2.7 in Fiji.
In current Fiji, the Jython implementation of os.path.exists(path) raises a
java.lang.AbstractMethodError iff 'path' doesn't exist. This function
catches the exception to allow normal usage of the exists() call.
"""
try:
return os.path.exists(path)
except java.lang.AbstractMethodError:
return False
# pylint: disable-msg=C0103
# we use the variable name 'exists' in its common spelling (lowercase), so
# removing this workaround will be straightforward at a later point
if platform.python_implementation() == 'Jython':
# pylint: disable-msg=F0401
# java.lang is only importable within Jython, pylint would complain
import java.lang
exists = jython_fiji_exists
else:
exists = os.path.exists
"""String related helper functions."""
# this is taken from numpy's iotools:
def _is_string_like(obj):
"""Check whether obj behaves like a string.
Using this way of checking for a string-like object is more robust when
dealing with stuff that can behave like a 'str' but is not strictly an
instance of it (or a subclass thereof). So it's more generic than using
isinstance(obj, str).
Example
-------
>>> _is_string_like('foo')
True
>>> _is_string_like(123)
False
"""
try:
obj + ''
except (TypeError, ValueError):
return False
return True
def filename(name):
"""Get the filename from either a filehandle or a string.
This is a convenience function to retrieve the filename as a string given
either an open filehandle or just a plain str containing the name.
Parameters
----------
name : str or filehandle
Returns
-------
name : str
Example
-------
>>> filename('test_file_name')
'test_file_name'
>>> filename(open(__file__, 'r'))
'strtools.py'
"""
if isinstance(name, file):
return name.name
elif _is_string_like(name):
return name
else:
raise TypeError
def flatten(lst):
"""Make a single string from a list of strings.
Parameters
----------
lst : list(str)
Returns
-------
flat : str
Example
-------
>>> flatten(('foo', 'bar'))
'foobar'
"""
flat = ""
for line in lst:
flat += line
return flat
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment