transparentpath


Nametransparentpath JSON
Version 0.3.229 PyPI version JSON
download
home_pagehttps://github.com/Advestis/transparentpath
SummaryA class that allows one to use a path in a local file system or a gcs file system (more or less) in almost the same way one would use a pathlib.Path object.
upload_time2021-05-07 09:56:15
maintainer
docs_urlNone
authorPhilippe COTTE
requires_python>=3.7
license
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ---
permalink: /docs/index.html
---

**The complete documentation is available at https://advestis.github.io/transparentpath/**

# TransparentPath

A class that allows one to use a path in a local file system or a gcs file system (more or less) in almost the
same way one would use a pathlib.Path object.

## Requirements

You will need credential .json file, that you can set in the envvar GOOGLE_APPLICATION_CREDENTIALS.
If your python code is launched in a google cloud instance (VM, pods, etc...), GOOGLE_APPLICATION_CREDENTIALS should 
be set by default. 
 
## Installation

You can install this package with pip :

    pip install transparentpath-nightly

Or use it in a Dockerfile:

    FROM advestis/transparentpath-nightly
    ...

## Optional packages

The vanilla version allows you to declare paths and work with them. You can use them in the builtin `open` method. 
Optionally, you can also install support for several other packages like pandas, dask, etc... the currently 
available optionnal packages are accessible through the follownig commands: 

    pip install transparentpath-nightly[pandas]
    pip install transparentpath-nightly[parquet]
    pip install transparentpath-nightly[hdf5]
    pip install transparentpath-nightly[json]
    pip install transparentpath-nightly[excel]
    pip install transparentpath-nightly[dask]

you can install all of those at once

    pip install transparentpath-nightly[all]

## Usage

Set TransparentPath to point to GCS:
```python
from transparentpath import TransparentPath as Path
Path.set_global_fs("gcs", bucket="bucket_name")
mypath = Path("foo") / "bar"  # Will use GCS
local_path = Path("chien", fs="local")  # will NOT use GCS
other_path = mypath / "stuff"  # Will use GCS
other_path_2 = local_path / "stuff"  # Will NOT use GCS
```

or

```python
from transparentpath import TransparentPath as Path
mypath = Path("foo", fs='gcs', bucket="my_bucket_name")  # Will use GCS
local_path = Path("chien", fs="local")  # will NOT use GCS 
other_local_path = Path("foo2")  # will NOT use GCS
```

or

```python
# noinspection PyShadowingNames
from transparentpath import TransparentPath as Path
mypath = Path("gs://my_bucket_name/foo")  # Will use GCS
other_path = Path("foo2")  # will NOT use GCS
```

No matter whether you are using GCS or your local file system, the following commands are valid:

```python
from transparentpath import TransparentPath as Path
# Path.set_global_fs("gcs", bucket="bucket_name", project="project_name")
# The following lines will also work with the previous line uncommented 

# Reading a csv into a pandas' DataFrame and saving it as a parquet file
mypath = Path("foo") / "bar.csv"
df = mypath.read(index_col=0, parse_dates=True)
otherpath = mypath.with_suffix(".parquet")
otherpath.write(df)

# Reading and writing a HDF5 file works on GCS and on local:
import numpy as np
mypath = Path("foo") / "bar.hdf5"  # can be .h5 too
with mypath.read() as ifile:
    arr = np.array(ifile["store1"])

# Doing '..' from 'foo/bar.hdf5' will return 'foo'
# Then doing 'foo' + 'babar.hdf5' will return 'foo/babar.hdf5' ('+' and '/' are synonymes)
mypath.cd("..")  # Does not return a path but modifies inplace
with (mypath  + "babar.hdf5").write(None) as ofile:
    # Note here that we must explicitely give 'None' to the 'write' method in order for it
    # to return the open HDF5 file. We could also give a dict of {arr: "store1"} to directly
    # write the file.
    ofile["store1"] = arr


# Reading a text file. Can also use 'w', 'a', etc... also works with binaries.
mypath = Path("foo") / "bar.txt"
with open(mypath, "r") as ifile:
    lines = ifile.readlines()

# open is overriden to understand gs://
with open("gs://bucket/file.txt", "r") as ifile:
    lines = ifile.readlines()

mypath.is_file()
mypath.is_dir()  # Specific behavior on GCS. See 'Behavior' below.
mypath.is_file()
files = mypath.parent.glob("*.csv")  # Returns a Iterator[TransparentPath], can be casted to list
```

As you can see from the previous example, all methods returning a path from a TransparentPath return a 
TransparentPath.

### Dask

TransparentPath supports writing and reading Dask dataframes from and to csv, excel, parquet and HDF5, both locally and
remotely. You need to have dask-dataframe and dask-distributed installed, which will be the case if you ran `pip 
install transparentpath-nightly[dask]`. Writing Dask dataframes does not require any additionnal arguments to be passed
for the type will be checked before calling the appropriate writting method. Reading however requires you to pass 
the *use_dask* argument to the `read()` method. If the file to read is HDF5, you will also need to specify 
*set_names*, matching the argument *key* of Dask's `read_hdf()` method.

Note that if reading a remote HDF5, the file will be downloaded in your local tmp, then read. If not using Dask, the 
file is deleted after being read. But since Dask uses delayed processes, deleting the file might occure before the 
file is actually read, so the file is kept. Up to you to empty your /tmp directory if it is not done automatically 
by your system.


Do not hesitate to read the documentation in **docs/** for more details on each method.


## Behavior

All instances of TransparentPath are absolute, even if created with relative paths.

TransparentPaths are seen as instances of str: 

```python
from transparentpath import TransparentPath as Path
path = Path()
isinstance(path, str)  # returns True
```
 
This is required to allow
 
```python
from transparentpath import TransparentPath as Path
path = Path()
with open(path(), "w/r/a/b...") as ifile:
    ...
```
to work. If you want to check whether path is actually a TransparentPath and nothing else, use 

```python
from transparentpath import TransparentPath as Path
path = Path()
type(path) == Path  # returns True
```
instead.

Note that your script must be able to log to GCS somehow. As mentionned before, you can use a service account json 
file by setting the env var 
`GOOGLE_APPLICATION_CREDENTIALS=path_to_project_cred.json`
in your .bashrc. You can also do it from within your python code with `os.environ["GOOGLE_APPLICATION_CREDENTIALS"]
=path_to_project_cred.json`. The last method is:

```python
from transparentpath import TransparentPath as Path
Path.set_global_fs("gcs", bucket="bucket", token="path_to_project_cred.json")
# AND/OR
path = Path("gs://bucket/file", token="path_to_project_cred.json")
```

If your code is running on a VM or pod on GCP, you do not need to provide any credentials.

Since the bucket name is provided in set_global_fs, you **must not** specify it in your paths unless you also 
include "gs://" in front of it. You should never create a path with a directory with the same name as your current 
bucket.

If your directories architecture on GCS is the same than localy up to some root directory, you can do:

```python
from transparentpath import TransparentPath as Path
Path.nas_dir = "/media/SERVEUR" # Example root path that differs between local and GCS architecture
Path.set_global_fs("gcs", bucket="my_bucket")
p = Path("/media/SERVEUR") / "chien" / "chat"  # Will be gs://my_bucket/chien/chat
```

If the line *Path.set_global_fs(...* is not commented out, the resulting path will be *gs://my_bucket/chien/chat*.
If the line *Path.set_global_fs(...* is commented out, the resulting path will be */media/SERVEUR/chien/chat*.

This allows you to create codes that can run identically both localy and on gcs, the only difference being
the line 'Path.set_global_fs(...'.

Any method or attribute valid in fsspec.implementations.local.LocalFileSystem, gcs.GCSFileSystem or pathlib.Path
can be used on a TransparentPath object.

## Warnings

### Warnings about GCS behaviour
if you use GCS:

  1. Remember that directories are not a thing on GCS.

  2. The is_dir() method exists but, on GCS, only makes sense if tested on a part of an existing path,
  i.e not on a leaf.

  3. You do not need the parent directories of a file to create the file : they will be created if they do not
  exist (that is not true localy however).

  4. If you delete a file that was alone in its parent directories, those directories disapear.

  5. Since most of the times we use is_dir() we want to check whether a directry exists to write in it,
  by default the is_dir() method will return True if the directory does not exists on GCS (see point 3)(will
  still return false if using a local file system). The only case is_dir() will return False is if a file with
  the same name exists (localy, behavior is straightforward). To actually check whether the directory exists (
  for, like, reading from it), add the kwarg 'exist=True' to is_dir() if using GCS.

  6. If a file exists at the same path than a directory, then the class is not able to know which one is the
  file and which one is the directory, and will raise a TPMultipleExistenceError upon object creation. Will also
  check for multiplicity at almost every method in case an exterior source created a duplicate of the
  file/directory. This case can't happen locally. However, it can happen on remote if the cache is not updated
  frequently. Donig this check can significantly increase computation time (if using glob on a directory
  containing a lot of files for example). You can deactivate it either globally (TransparentPath._do_check =
  False and TransparentPath._do_update_cache = False), for a specific path (pass nockeck=True at path
  creation), or for glob and ls by passing fast=True as additional argument.


### Speed

TransparentPath on GCS is slow because of the verification for multiple existance and the cache updating.
However one can tweak those a bit. As mentionned earlier, cache updating and multiple existence check can be
deactivated for all paths by doing

```python
from transparentpath import TransparentPath
TransparentPath._do_update_cache = False
TransparentPath._do_check = False
```

They can also be deactivated for one path only by doing

```python
p = TransparentPath("somepath", nocheck=True, notupdatecache=True)
```

It is also possible to specify when to do those check : at path creation, path usage (read, write, exists...) or 
both. Here to it can be set on all paths or only some : 

```python
TransparentPath._when_checked = {"created": True, "used": False}  # Default value
TransparentPath._when_updated = {"created": True, "used": False}  # Default value
p = TransparentPath("somepath", when_checked={"created": False, "used": False},
                    notupdatecache={"created": False, "used": False})
```

There is also an expiration time in seconds for check and update : the operation is not done if it was done not a
long time ago. Those expiration times are of 1 second by default and can be changed through :

```python
TransparentPath._check_expire = 10
TransparentPath._update_expire = 10
p = TransparentPath("somepath", check_expire=0, update_expire=0)
```

glob() and ls() have their own way to be accelerated : 

```python
p.glob("/*", fast=True)
p.ls("", fast=True)
```

Basically, fast=True means do not check and do not update the cache for all the items found by the method.

All paths created from another path will share its parent's attributes : 
 * fs_kind
 * bucket
 * notupdatecache
 * nocheck
 * when_checked
 * when_updated
 * update_expire
 * check_expire
 * token


### os.open

os.open is overloaded by TransparentPath to support giving a TransparentPath to it. If a method in a package you did 
not create uses the os.open() in a *with* statement, everything should work out of the box with a TransparentPath. 

However, if it uses the **output** of os.open, you will have to create a class to 
override this method and anything using its ouput. Indeed, os.open returns a file descriptor, not an IO, and I did 
not find a way to access file descriptors on gcs. For example, in the FileLock package, the acquire() method calls the
_acquire() method which calls os.open(), so I had to do that:

```python
from filelock import FileLock
from transparentpath import TransparentPath as Path

class MyFileLock(FileLock):
    def _acquire(self):
        tmp_lock_file = self._lock_file
        if not type(tmp_lock_file) == Path:
            tmp_lock_file = Path(tmp_lock_file)
        try:
            fd = tmp_lock_file.open("x")
        except (IOError, OSError, FileExistsError):
            pass
        else:
            self._lock_file_fd = fd
        return None
```

The original method was:

```python
import os
...
def _acquire(self):
    open_mode = os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_TRUNC
    try:
        fd = os.open(self._lock_file, open_mode)
    except (IOError, OSError):
        pass
    else:
        self._lock_file_fd = fd
    return None
...
```

I tried to implement a working version of any method valid in pathlib.Path or in file systems, but futur changes
in any of those will not be taken into account quickly.
            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/Advestis/transparentpath",
    "name": "transparentpath",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "",
    "author": "Philippe COTTE",
    "author_email": "pcotte@advestis.com",
    "download_url": "https://files.pythonhosted.org/packages/73/7d/358cc2629d1e0326de80d3cf88b3b36e15f23769cf1eb4000c5fbacfe747/transparentpath-0.3.229.tar.gz",
    "platform": "",
    "description": "---\npermalink: /docs/index.html\n---\n\n**The complete documentation is available at https://advestis.github.io/transparentpath/**\n\n# TransparentPath\n\nA class that allows one to use a path in a local file system or a gcs file system (more or less) in almost the\nsame way one would use a pathlib.Path object.\n\n## Requirements\n\nYou will need credential .json file, that you can set in the envvar GOOGLE_APPLICATION_CREDENTIALS.\nIf your python code is launched in a google cloud instance (VM, pods, etc...), GOOGLE_APPLICATION_CREDENTIALS should \nbe set by default. \n \n## Installation\n\nYou can install this package with pip :\n\n    pip install transparentpath-nightly\n\nOr use it in a Dockerfile:\n\n    FROM advestis/transparentpath-nightly\n    ...\n\n## Optional packages\n\nThe vanilla version allows you to declare paths and work with them. You can use them in the builtin `open` method. \nOptionally, you can also install support for several other packages like pandas, dask, etc... the currently \navailable optionnal packages are accessible through the follownig commands: \n\n    pip install transparentpath-nightly[pandas]\n    pip install transparentpath-nightly[parquet]\n    pip install transparentpath-nightly[hdf5]\n    pip install transparentpath-nightly[json]\n    pip install transparentpath-nightly[excel]\n    pip install transparentpath-nightly[dask]\n\nyou can install all of those at once\n\n    pip install transparentpath-nightly[all]\n\n## Usage\n\nSet TransparentPath to point to GCS:\n```python\nfrom transparentpath import TransparentPath as Path\nPath.set_global_fs(\"gcs\", bucket=\"bucket_name\")\nmypath = Path(\"foo\") / \"bar\"  # Will use GCS\nlocal_path = Path(\"chien\", fs=\"local\")  # will NOT use GCS\nother_path = mypath / \"stuff\"  # Will use GCS\nother_path_2 = local_path / \"stuff\"  # Will NOT use GCS\n```\n\nor\n\n```python\nfrom transparentpath import TransparentPath as Path\nmypath = Path(\"foo\", fs='gcs', bucket=\"my_bucket_name\")  # Will use GCS\nlocal_path = Path(\"chien\", fs=\"local\")  # will NOT use GCS \nother_local_path = Path(\"foo2\")  # will NOT use GCS\n```\n\nor\n\n```python\n# noinspection PyShadowingNames\nfrom transparentpath import TransparentPath as Path\nmypath = Path(\"gs://my_bucket_name/foo\")  # Will use GCS\nother_path = Path(\"foo2\")  # will NOT use GCS\n```\n\nNo matter whether you are using GCS or your local file system, the following commands are valid:\n\n```python\nfrom transparentpath import TransparentPath as Path\n# Path.set_global_fs(\"gcs\", bucket=\"bucket_name\", project=\"project_name\")\n# The following lines will also work with the previous line uncommented \n\n# Reading a csv into a pandas' DataFrame and saving it as a parquet file\nmypath = Path(\"foo\") / \"bar.csv\"\ndf = mypath.read(index_col=0, parse_dates=True)\notherpath = mypath.with_suffix(\".parquet\")\notherpath.write(df)\n\n# Reading and writing a HDF5 file works on GCS and on local:\nimport numpy as np\nmypath = Path(\"foo\") / \"bar.hdf5\"  # can be .h5 too\nwith mypath.read() as ifile:\n    arr = np.array(ifile[\"store1\"])\n\n# Doing '..' from 'foo/bar.hdf5' will return 'foo'\n# Then doing 'foo' + 'babar.hdf5' will return 'foo/babar.hdf5' ('+' and '/' are synonymes)\nmypath.cd(\"..\")  # Does not return a path but modifies inplace\nwith (mypath  + \"babar.hdf5\").write(None) as ofile:\n    # Note here that we must explicitely give 'None' to the 'write' method in order for it\n    # to return the open HDF5 file. We could also give a dict of {arr: \"store1\"} to directly\n    # write the file.\n    ofile[\"store1\"] = arr\n\n\n# Reading a text file. Can also use 'w', 'a', etc... also works with binaries.\nmypath = Path(\"foo\") / \"bar.txt\"\nwith open(mypath, \"r\") as ifile:\n    lines = ifile.readlines()\n\n# open is overriden to understand gs://\nwith open(\"gs://bucket/file.txt\", \"r\") as ifile:\n    lines = ifile.readlines()\n\nmypath.is_file()\nmypath.is_dir()  # Specific behavior on GCS. See 'Behavior' below.\nmypath.is_file()\nfiles = mypath.parent.glob(\"*.csv\")  # Returns a Iterator[TransparentPath], can be casted to list\n```\n\nAs you can see from the previous example, all methods returning a path from a TransparentPath return a \nTransparentPath.\n\n### Dask\n\nTransparentPath supports writing and reading Dask dataframes from and to csv, excel, parquet and HDF5, both locally and\nremotely. You need to have dask-dataframe and dask-distributed installed, which will be the case if you ran `pip \ninstall transparentpath-nightly[dask]`. Writing Dask dataframes does not require any additionnal arguments to be passed\nfor the type will be checked before calling the appropriate writting method. Reading however requires you to pass \nthe *use_dask* argument to the `read()` method. If the file to read is HDF5, you will also need to specify \n*set_names*, matching the argument *key* of Dask's `read_hdf()` method.\n\nNote that if reading a remote HDF5, the file will be downloaded in your local tmp, then read. If not using Dask, the \nfile is deleted after being read. But since Dask uses delayed processes, deleting the file might occure before the \nfile is actually read, so the file is kept. Up to you to empty your /tmp directory if it is not done automatically \nby your system.\n\n\nDo not hesitate to read the documentation in **docs/** for more details on each method.\n\n\n## Behavior\n\nAll instances of TransparentPath are absolute, even if created with relative paths.\n\nTransparentPaths are seen as instances of str: \n\n```python\nfrom transparentpath import TransparentPath as Path\npath = Path()\nisinstance(path, str)  # returns True\n```\n \nThis is required to allow\n \n```python\nfrom transparentpath import TransparentPath as Path\npath = Path()\nwith open(path(), \"w/r/a/b...\") as ifile:\n    ...\n```\nto work. If you want to check whether path is actually a TransparentPath and nothing else, use \n\n```python\nfrom transparentpath import TransparentPath as Path\npath = Path()\ntype(path) == Path  # returns True\n```\ninstead.\n\nNote that your script must be able to log to GCS somehow. As mentionned before, you can use a service account json \nfile by setting the env var \n`GOOGLE_APPLICATION_CREDENTIALS=path_to_project_cred.json`\nin your .bashrc. You can also do it from within your python code with `os.environ[\"GOOGLE_APPLICATION_CREDENTIALS\"]\n=path_to_project_cred.json`. The last method is:\n\n```python\nfrom transparentpath import TransparentPath as Path\nPath.set_global_fs(\"gcs\", bucket=\"bucket\", token=\"path_to_project_cred.json\")\n# AND/OR\npath = Path(\"gs://bucket/file\", token=\"path_to_project_cred.json\")\n```\n\nIf your code is running on a VM or pod on GCP, you do not need to provide any credentials.\n\nSince the bucket name is provided in set_global_fs, you **must not** specify it in your paths unless you also \ninclude \"gs://\" in front of it. You should never create a path with a directory with the same name as your current \nbucket.\n\nIf your directories architecture on GCS is the same than localy up to some root directory, you can do:\n\n```python\nfrom transparentpath import TransparentPath as Path\nPath.nas_dir = \"/media/SERVEUR\" # Example root path that differs between local and GCS architecture\nPath.set_global_fs(\"gcs\", bucket=\"my_bucket\")\np = Path(\"/media/SERVEUR\") / \"chien\" / \"chat\"  # Will be gs://my_bucket/chien/chat\n```\n\nIf the line *Path.set_global_fs(...* is not commented out, the resulting path will be *gs://my_bucket/chien/chat*.\nIf the line *Path.set_global_fs(...* is commented out, the resulting path will be */media/SERVEUR/chien/chat*.\n\nThis allows you to create codes that can run identically both localy and on gcs, the only difference being\nthe line 'Path.set_global_fs(...'.\n\nAny method or attribute valid in fsspec.implementations.local.LocalFileSystem, gcs.GCSFileSystem or pathlib.Path\ncan be used on a TransparentPath object.\n\n## Warnings\n\n### Warnings about GCS behaviour\nif you use GCS:\n\n  1. Remember that directories are not a thing on GCS.\n\n  2. The is_dir() method exists but, on GCS, only makes sense if tested on a part of an existing path,\n  i.e not on a leaf.\n\n  3. You do not need the parent directories of a file to create the file : they will be created if they do not\n  exist (that is not true localy however).\n\n  4. If you delete a file that was alone in its parent directories, those directories disapear.\n\n  5. Since most of the times we use is_dir() we want to check whether a directry exists to write in it,\n  by default the is_dir() method will return True if the directory does not exists on GCS (see point 3)(will\n  still return false if using a local file system). The only case is_dir() will return False is if a file with\n  the same name exists (localy, behavior is straightforward). To actually check whether the directory exists (\n  for, like, reading from it), add the kwarg 'exist=True' to is_dir() if using GCS.\n\n  6. If a file exists at the same path than a directory, then the class is not able to know which one is the\n  file and which one is the directory, and will raise a TPMultipleExistenceError upon object creation. Will also\n  check for multiplicity at almost every method in case an exterior source created a duplicate of the\n  file/directory. This case can't happen locally. However, it can happen on remote if the cache is not updated\n  frequently. Donig this check can significantly increase computation time (if using glob on a directory\n  containing a lot of files for example). You can deactivate it either globally (TransparentPath._do_check =\n  False and TransparentPath._do_update_cache = False), for a specific path (pass nockeck=True at path\n  creation), or for glob and ls by passing fast=True as additional argument.\n\n\n### Speed\n\nTransparentPath on GCS is slow because of the verification for multiple existance and the cache updating.\nHowever one can tweak those a bit. As mentionned earlier, cache updating and multiple existence check can be\ndeactivated for all paths by doing\n\n```python\nfrom transparentpath import TransparentPath\nTransparentPath._do_update_cache = False\nTransparentPath._do_check = False\n```\n\nThey can also be deactivated for one path only by doing\n\n```python\np = TransparentPath(\"somepath\", nocheck=True, notupdatecache=True)\n```\n\nIt is also possible to specify when to do those check : at path creation, path usage (read, write, exists...) or \nboth. Here to it can be set on all paths or only some : \n\n```python\nTransparentPath._when_checked = {\"created\": True, \"used\": False}  # Default value\nTransparentPath._when_updated = {\"created\": True, \"used\": False}  # Default value\np = TransparentPath(\"somepath\", when_checked={\"created\": False, \"used\": False},\n                    notupdatecache={\"created\": False, \"used\": False})\n```\n\nThere is also an expiration time in seconds for check and update : the operation is not done if it was done not a\nlong time ago. Those expiration times are of 1 second by default and can be changed through :\n\n```python\nTransparentPath._check_expire = 10\nTransparentPath._update_expire = 10\np = TransparentPath(\"somepath\", check_expire=0, update_expire=0)\n```\n\nglob() and ls() have their own way to be accelerated : \n\n```python\np.glob(\"/*\", fast=True)\np.ls(\"\", fast=True)\n```\n\nBasically, fast=True means do not check and do not update the cache for all the items found by the method.\n\nAll paths created from another path will share its parent's attributes : \n * fs_kind\n * bucket\n * notupdatecache\n * nocheck\n * when_checked\n * when_updated\n * update_expire\n * check_expire\n * token\n\n\n### os.open\n\nos.open is overloaded by TransparentPath to support giving a TransparentPath to it. If a method in a package you did \nnot create uses the os.open() in a *with* statement, everything should work out of the box with a TransparentPath. \n\nHowever, if it uses the **output** of os.open, you will have to create a class to \noverride this method and anything using its ouput. Indeed, os.open returns a file descriptor, not an IO, and I did \nnot find a way to access file descriptors on gcs. For example, in the FileLock package, the acquire() method calls the\n_acquire() method which calls os.open(), so I had to do that:\n\n```python\nfrom filelock import FileLock\nfrom transparentpath import TransparentPath as Path\n\nclass MyFileLock(FileLock):\n    def _acquire(self):\n        tmp_lock_file = self._lock_file\n        if not type(tmp_lock_file) == Path:\n            tmp_lock_file = Path(tmp_lock_file)\n        try:\n            fd = tmp_lock_file.open(\"x\")\n        except (IOError, OSError, FileExistsError):\n            pass\n        else:\n            self._lock_file_fd = fd\n        return None\n```\n\nThe original method was:\n\n```python\nimport os\n...\ndef _acquire(self):\n    open_mode = os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_TRUNC\n    try:\n        fd = os.open(self._lock_file, open_mode)\n    except (IOError, OSError):\n        pass\n    else:\n        self._lock_file_fd = fd\n    return None\n...\n```\n\nI tried to implement a working version of any method valid in pathlib.Path or in file systems, but futur changes\nin any of those will not be taken into account quickly.",
    "bugtrack_url": null,
    "license": "",
    "summary": "A class that allows one to use a path in a local file system or a gcs file system (more or less) in almost the same way one would use a pathlib.Path object.",
    "version": "0.3.229",
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "md5": "cfb7477c57c261e7a5c4947753dacd52",
                "sha256": "2b969dc049024129f5a98b31054ed24426c88b76040324cd7cea84d7ba855041"
            },
            "downloads": -1,
            "filename": "transparentpath-0.3.229.tar.gz",
            "has_sig": false,
            "md5_digest": "cfb7477c57c261e7a5c4947753dacd52",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 6739619,
            "upload_time": "2021-05-07T09:56:15",
            "upload_time_iso_8601": "2021-05-07T09:56:15.955773Z",
            "url": "https://files.pythonhosted.org/packages/73/7d/358cc2629d1e0326de80d3cf88b3b36e15f23769cf1eb4000c5fbacfe747/transparentpath-0.3.229.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2021-05-07 09:56:15",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": null,
    "github_project": "Advestis",
    "error": "Could not fetch GitHub repository",
    "lcname": "transparentpath"
}
        
Elapsed time: 0.29775s