Skip to content

Commit

Permalink
implementation of glob (#209)
Browse files Browse the repository at this point in the history
* implementation of glob

* typing, count and remove

* count lines

* Add tests

* tests

* Refactor iter

* docs

* docs and refactor

* polish

* doc fix

* doc

* doc fix

* fix and docs

* version bump
  • Loading branch information
willmcgugan authored Aug 12, 2018
1 parent 2f305b8 commit 9e4d4b9
Show file tree
Hide file tree
Showing 17 changed files with 525 additions and 30 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [2.1.0] - 2018-08-12

### Added

- fs.glob support

## [2.0.27] - 2018-08-05

### Fixed
Expand Down Expand Up @@ -159,6 +165,7 @@ No changes, pushed wrong branch to PyPi.
## [2.0.8] - 2017-08-13

### Added

- Lstat info namespace
- Link info namespace
- FS.islink method
Expand Down
2 changes: 2 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,5 @@

# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False

napoleon_include_special_with_doc = True
72 changes: 72 additions & 0 deletions docs/source/globbing.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
.. _globbing:

Globbing
========

Globbing is the process of matching paths according to the rules used
by the Unix shell.

Generally speaking, you can think of a glob pattern as a path containing
one or more wildcard patterns, separated by forward slashes.


Matching Files and Directories
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In a glob pattern, A ``*`` means match anything text in a filename. A ``?``
matches any single character. A ``**`` matches any number of subdirectories,
making the glob *recusrive*. If the glob pattern ends in a ``/``, it will
only match directory paths, otherwise it will match files and directories.

.. note::
A recursive glob requires that PyFilesystem scan a lot of files,
and can potentially be slow for large (or network based) filesystems.

Here's a summary of glob patterns:

``*``
Matches all files in the current directory.
``*.py``
Matches all .py file in the current directory.
``*.py?``
Matches all .py files and .pyi, .pyc etc in the currenct directory.
``project/*.py``
Matches all .py files in a directory called ``project``.
``*/*.py``
Matches all .py files in any sub directory.
``**/*.py``
Recursively matches all .py files.
``**/.git/``
Recursively matches all the git directories.


Interface
~~~~~~~~~

PyFilesystem supports globbing via the ``glob`` attribute on every FS
instance, which is an instance of :class:`~fs.glob.BoundGlobber`. Here's
how you might use it to find all the Python files in your filesystem::

for match in my_fs.glob("**/*.py"):
print(f"{match.path} is {match.info.size} bytes long")

Calling ``.glob`` with a pattern will return an iterator of
:class:`~fs.glob.GlobMatch` named tuples for each matching file or
directory. A glob match contains two attributes; ``path`` which is the
full path in the filesystem, and ``info`` which is an
:class:`fs.info.Info` info object for the matched resource.


Batch Methods
~~~~~~~~~~~~~

In addition to iterating over the results, you can also call methods on
the :class:`~fs.glob.Globber` which apply to every matched path.

For instance, here is how you can use glob to remove all ``.pyc`` files
from a project directory::

>>> import fs
>>> fs.open_fs('~/projects/my_project').glob('**/*.pyc').remove()
29

14 changes: 14 additions & 0 deletions docs/source/guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,20 @@ The ``walk`` attribute on FS objects is instance of a :class:`~fs.walk.BoundWalk

See :ref:`walking` for more information on walking directories.

Globbing
~~~~~~~~

Closely related to walking a filesystem is *globbing*, which is a slightly higher level way of scanning filesystems. Paths can be filtered by a *glob* pattern, which is similar to a wildcard (such as ``*.py``), but can match multiple levels of a directory structure.

Here's an example of globbing, which removes all the ``.pyc`` files in your project directory::

>>> from fs import open_fs
>>> open_fs('~/project').glob('**/*.pyc').remove()
62

See :ref:`globbing` for more information.


Moving and Copying
~~~~~~~~~~~~~~~~~~

Expand Down
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Contents:
info.rst
openers.rst
walking.rst
globbing.rst
builtin.rst
implementers.rst
extension.rst
Expand Down
1 change: 1 addition & 0 deletions docs/source/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Reference
reference/copy.rst
reference/enums.rst
reference/errors.rst
reference/glob.rst
reference/info_objects.rst
reference/filesize.rst
reference/mirror.rst
Expand Down
5 changes: 5 additions & 0 deletions docs/source/reference/glob.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fs.glob
=======

.. automodule:: fs.glob
:members:
2 changes: 1 addition & 1 deletion fs/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Version, used in module and setup.py.
"""
__version__ = "2.0.27"
__version__ = "2.1.0"
29 changes: 12 additions & 17 deletions fs/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,23 @@
"""

from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import absolute_import, print_function, unicode_literals

import abc
import itertools
import os
import threading
import time
import typing
from functools import partial

from contextlib import closing
import itertools
from functools import partial

import six

from . import copy
from . import errors
from . import fsencode
from . import iotools
from . import move
from . import tools
from . import walk
from . import wildcard
from . import copy, errors, fsencode, iotools, move, tools, walk, wildcard
from .glob import BoundGlobber
from .mode import validate_open_mode
from .path import abspath
from .path import join
from .path import normpath
from .path import abspath, join, normpath
from .time import datetime_to_epoch
from .walk import Walker

Expand Down Expand Up @@ -108,6 +97,12 @@ def __exit__(
"""
self.close()

@property
def glob(self):
"""`~fs.glob.BoundGlobber`: a globber object..
"""
return BoundGlobber(self)

@property
def walk(self):
# type: (_F) -> BoundWalker[_F]
Expand Down
6 changes: 3 additions & 3 deletions fs/copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def copy_fs_if_newer(
on_copy (callable):A function callback called after a single file copy
is executed. Expected signature is ``(src_fs, src_path, dst_fs,
dst_path)``.
workers (int): Use `worker` threads to copy data, or ``0`` (default) for
workers (int): Use ``worker`` threads to copy data, or ``0`` (default) for
a single-threaded copy.
"""
Expand Down Expand Up @@ -269,7 +269,7 @@ def copy_dir(
on_copy (callable, optional): A function callback called after
a single file copy is executed. Expected signature is
``(src_fs, src_path, dst_fs, dst_path)``.
workers (int): Use `worker` threads to copy data, or ``0`` (default) for
workers (int): Use ``worker`` threads to copy data, or ``0`` (default) for
a single-threaded copy.
"""
Expand Down Expand Up @@ -330,7 +330,7 @@ def copy_dir_if_newer(
on_copy (callable, optional): A function callback called after
a single file copy is executed. Expected signature is
``(src_fs, src_path, dst_fs, dst_path)``.
workers (int): Use `worker` threads to copy data, or ``0`` (default) for
workers (int): Use ``worker`` threads to copy data, or ``0`` (default) for
a single-threaded copy.
"""
Expand Down
Loading

0 comments on commit 9e4d4b9

Please sign in to comment.