238 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
			
		
		
	
	
			238 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
| =======
 | |
| py.path
 | |
| =======
 | |
| 
 | |
| .. contents::
 | |
| .. sectnum::
 | |
| 
 | |
| The 'py' lib provides a uniform high-level api to deal with filesystems
 | |
| and filesystem-like interfaces: :api:`py.path`.  It aims to offer a central
 | |
| object to fs-like object trees (reading from and writing to files, adding
 | |
| files/directories, examining the types and structure, etc.), and out-of-the-box
 | |
| provides a number of implementations of this API.
 | |
| 
 | |
| Path implementations provided by :api:`py.path`
 | |
| ===============================================
 | |
| 
 | |
| :api:`py.path.local`
 | |
| --------------------
 | |
| 
 | |
| The first and most obvious of the implementations is a wrapper around a local
 | |
| filesystem. It's just a bit nicer in usage than the regular Python APIs, and
 | |
| of course all the functionality is bundled together rather than spread over a
 | |
| number of modules.
 | |
| 
 | |
| Example usage, here we use the :api:`py.test.ensuretemp()` function to create
 | |
| a :api:`py.path.local` object for us (which wraps a directory)::
 | |
| 
 | |
|   >>> import py
 | |
|   >>> temppath = py.test.ensuretemp('py.path_documentation')
 | |
|   >>> foopath = temppath.join('foo') # get child 'foo' (lazily)
 | |
|   >>> foopath.check() # check if child 'foo' exists
 | |
|   False
 | |
|   >>> foopath.write('bar') # write some data to it
 | |
|   >>> foopath.check()
 | |
|   True
 | |
|   >>> foopath.read()
 | |
|   'bar'
 | |
|   >>> foofile = foopath.open() # return a 'real' file object
 | |
|   >>> foofile.read(1)
 | |
|   'b'
 | |
| 
 | |
| :api:`py.path.svnurl` and :api:`py.path.svnwc`
 | |
| ----------------------------------------------
 | |
| 
 | |
| Two other :api:`py.path` implementations that the py lib provides wrap the
 | |
| popular `Subversion`_ revision control system: the first (called 'svnurl')
 | |
| by interfacing with a remote server, the second by wrapping a local checkout.
 | |
| Both allow you to access relatively advanced features such as metadata and
 | |
| versioning, and both in a way more user-friendly manner than existing other
 | |
| solutions.
 | |
| 
 | |
| Some example usage of :api:`py.path.svnurl`::
 | |
| 
 | |
|   .. >>> import py
 | |
|   .. >>> if not getattr(py.test.config.option, 'checkremote', 0): py.test.skip("use --checkremote to enable svn remote doctests")
 | |
|   >>> url = py.path.svnurl('http://codespeak.net/svn/py')
 | |
|   >>> info = url.info()
 | |
|   >>> info.kind
 | |
|   'dir'
 | |
|   >>> firstentry = url.log()[-1]
 | |
|   >>> import time
 | |
|   >>> time.strftime('%Y-%m-%d', time.gmtime(firstentry.date))
 | |
|   '2004-10-02'
 | |
| 
 | |
| Example usage of :api:`py.path.svnwc`::
 | |
| 
 | |
|   >>> temp = py.test.ensuretemp('py.path_documentation')
 | |
|   >>> wc = py.path.svnwc(temp.join('svnwc'))
 | |
|   >>> wc.checkout('http://codespeak.net/svn/py/dist/py/path/local')
 | |
|   >>> wc.join('local.py').check()
 | |
|   True
 | |
| 
 | |
| .. _`Subversion`: http://subversion.tigris.org/
 | |
| 
 | |
| Common vs. specific API
 | |
| =======================
 | |
| 
 | |
| All Path objects support a common set of operations, suitable
 | |
| for many use cases and allowing to transparently switch the 
 | |
| path object within an application (e.g. from "local" to "svnwc"). 
 | |
| The common set includes functions such as `path.read()` to read all data 
 | |
| from a file, `path.write()` to write data, `path.listdir()` to get a list 
 | |
| of directory entries, `path.check()` to check if a node exists
 | |
| and is of a particular type, `path.join()` to get
 | |
| to a (grand)child, `path.visit()` to recursively walk through a node's
 | |
| children, etc.  Only things that are not common on 'normal' filesystems (yet), 
 | |
| such as handling metadata (e.g. the Subversion "properties") require
 | |
| using specific APIs. 
 | |
| 
 | |
| Examples
 | |
| ---------------------------------
 | |
| 
 | |
| A quick 'cookbook' of small examples that will be useful 'in real life',
 | |
| which also presents parts of the 'common' API, and shows some non-common
 | |
| methods:
 | |
| 
 | |
| Searching `.txt` files 
 | |
| +++++++++++++++++++++++++++++++++++++
 | |
| 
 | |
| Search for a particular string inside all files with a .txt extension in a
 | |
| specific directory.
 | |
| 
 | |
| ::
 | |
| 
 | |
|   >>> dirpath = temppath.ensure('testdir', dir=True)
 | |
|   >>> dirpath.join('textfile1.txt').write('foo bar baz')
 | |
|   >>> dirpath.join('textfile2.txt').write('frob bar spam eggs')
 | |
|   >>> subdir = dirpath.ensure('subdir', dir=True)
 | |
|   >>> subdir.join('textfile1.txt').write('foo baz')
 | |
|   >>> subdir.join('textfile2.txt').write('spam eggs spam foo bar spam')
 | |
|   >>> results = []
 | |
|   >>> for fpath in dirpath.visit('*.txt'):
 | |
|   ...     if 'bar' in fpath.read():
 | |
|   ...         results.append(fpath.basename)
 | |
|   >>> results
 | |
|   ['textfile1.txt', 'textfile2.txt', 'textfile2.txt']
 | |
| 
 | |
| Working with Paths 
 | |
| ++++++++++++++++++++
 | |
| 
 | |
| This example shows the :api:`py.path` features to deal with
 | |
| filesystem paths Note that the filesystem is never touched,
 | |
| all operations are performed on a string level (so the paths
 | |
| don't have to exist, either)::
 | |
| 
 | |
|   >>> p1 = py.path.local('/foo/bar')
 | |
|   >>> p2 = p1.join('baz/qux')
 | |
|   >>> p2 == py.path.local('/foo/bar/baz/qux')
 | |
|   True
 | |
|   >>> sep = py.path.local.sep
 | |
|   >>> p2.relto(p1).replace(sep, '/') # os-specific path sep in the string
 | |
|   'baz/qux'
 | |
|   >>> p3 = p1 / 'baz/qux' # the / operator allows joining, too
 | |
|   >>> p2 == p3
 | |
|   True
 | |
|   >>> p4 = p1 + ".py"  
 | |
|   >>> p4.basename == "bar.py"
 | |
|   True
 | |
|   >>> p4.ext == ".py"
 | |
|   True
 | |
|   >>> p4.purebasename == "bar"
 | |
|   True
 | |
| 
 | |
| This should be possible on every implementation of :api:`py.path`, so
 | |
| regardless of whether the implementation wraps a UNIX filesystem, a Windows
 | |
| one, or a database or object tree, these functions should be available (each
 | |
| with their own notion of path seperators and dealing with conversions, etc.).
 | |
| 
 | |
| Checking path types
 | |
| +++++++++++++++++++++
 | |
| 
 | |
| Now we will show a bit about the powerful 'check()' method on paths, which
 | |
| allows you to check whether a file exists, what type it is, etc.::
 | |
| 
 | |
|   >>> file1 = temppath.join('file1')
 | |
|   >>> file1.check() # does it exist?
 | |
|   False
 | |
|   >>> file1 = file1.ensure(file=True) # 'touch' the file
 | |
|   >>> file1.check()
 | |
|   True
 | |
|   >>> file1.check(dir=True) # is it a dir?
 | |
|   False
 | |
|   >>> file1.check(file=True) # or a file?
 | |
|   True
 | |
|   >>> file1.check(ext='.txt') # check the extension
 | |
|   False
 | |
|   >>> textfile = temppath.ensure('text.txt', file=True)
 | |
|   >>> textfile.check(ext='.txt')
 | |
|   True
 | |
|   >>> file1.check(basename='file1') # we can use all the path's properties here
 | |
|   True
 | |
| 
 | |
| Setting svn-properties 
 | |
| +++++++++++++++++++++++++++++++++++++++
 | |
| 
 | |
| As an example of 'uncommon' methods, we'll show how to read and write
 | |
| properties in an :api:`py.path.svnwc` instance::
 | |
| 
 | |
|   >>> wc.propget('foo')
 | |
|   ''
 | |
|   >>> wc.propset('foo', 'bar')
 | |
|   >>> wc.propget('foo')
 | |
|   'bar'
 | |
|   >>> len(wc.status().prop_modified) # our own props
 | |
|   1
 | |
|   >>> msg = wc.revert() # roll back our changes
 | |
|   >>> len(wc.status().prop_modified)
 | |
|   0
 | |
| 
 | |
| 
 | |
| Known problems / limitations
 | |
| ===================================
 | |
| 
 | |
| * The SVN path objects require the "svn" command line,
 | |
|   there is currently no support for python bindings. 
 | |
|   Parsing the svn output can lead to problems, particularly
 | |
|   regarding if you have a non-english "locales" setting. 
 | |
| 
 | |
| * While the path objects basically work on windows, 
 | |
|   there is no attention yet on making unicode paths
 | |
|   work or deal with the famous "8.3" filename issues. 
 | |
| 
 | |
| Future plans
 | |
| ============
 | |
| 
 | |
| The Subversion path implementations are based
 | |
| on the `svn` command line, not on the bindings. 
 | |
| It makes sense now to directly use the bindings. 
 | |
| 
 | |
| Moreover, it would be good, also considering 
 | |
| `py.execnet`_ distribution of programs, to
 | |
| be able to manipulate Windows Paths on Linux
 | |
| and vice versa.  So we'd like to consider
 | |
| refactoring the path implementations 
 | |
| to provide this choice (and getting rid
 | |
| of platform-dependencies as much as possible). 
 | |
| 
 | |
| There is some experimental small approach 
 | |
| (:source:`py/path/gateway/`) aiming at having
 | |
| a convenient Remote Path implementation
 | |
| and some considerations about future 
 | |
| works in the according :source:`py/path/gateway/TODO.txt`
 | |
| 
 | |
| There are various hacks out there to have
 | |
| Memory-Filesystems and even path objects
 | |
| being directly mountable under Linux (via `fuse`). 
 | |
| However, the Path object implementations 
 | |
| do not internally have a clean abstraction 
 | |
| of going to the filesystem - so with some
 | |
| refactoring it should become easier to 
 | |
| have very custom Path objects, still offering
 | |
| the quite full interface without requiring 
 | |
| to know about all details of the full path 
 | |
| implementation. 
 | |
| 
 | |
| .. _`py.execnet`: execnet.html
 | |
| 
 |