274 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
			
		
		
	
	
			274 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
=======
 | 
						|
py.path
 | 
						|
=======
 | 
						|
 | 
						|
The 'py' lib provides a uniform high-level api to deal with filesystems
 | 
						|
and filesystem-like interfaces: ``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 ``py.path``
 | 
						|
===============================================
 | 
						|
 | 
						|
.. _`local`: 
 | 
						|
 | 
						|
``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 ``py.test.ensuretemp()`` function to create
 | 
						|
a ``py.path.local`` object for us (which wraps a directory):
 | 
						|
 | 
						|
.. sourcecode:: pycon
 | 
						|
 | 
						|
  >>> 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'
 | 
						|
 | 
						|
``py.path.svnurl`` and ``py.path.svnwc``
 | 
						|
----------------------------------------------
 | 
						|
 | 
						|
Two other ``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 ``py.path.svnurl``:
 | 
						|
 | 
						|
.. sourcecode:: pycon
 | 
						|
 | 
						|
  .. >>> import py
 | 
						|
  .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk')
 | 
						|
  >>> 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 ``py.path.svnwc``:
 | 
						|
 | 
						|
.. sourcecode:: pycon
 | 
						|
 | 
						|
  .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk')
 | 
						|
  >>> 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.
 | 
						|
 | 
						|
.. sourcecode:: pycon
 | 
						|
 | 
						|
  >>> 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.sort()
 | 
						|
  >>> results
 | 
						|
  ['textfile1.txt', 'textfile2.txt', 'textfile2.txt']
 | 
						|
 | 
						|
Working with Paths 
 | 
						|
.......................
 | 
						|
 | 
						|
This example shows the ``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):
 | 
						|
 | 
						|
.. sourcecode:: pycon
 | 
						|
 | 
						|
  >>> 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'
 | 
						|
  >>> p2.bestrelpath(p1).replace(sep, '/')
 | 
						|
  '../..'
 | 
						|
  >>> p2.join(p2.bestrelpath(p1)) == p1 
 | 
						|
  True
 | 
						|
  >>> 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 ``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.:
 | 
						|
 | 
						|
.. sourcecode:: pycon
 | 
						|
 | 
						|
  >>> 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 ``py.path.svnwc`` instance:
 | 
						|
 | 
						|
.. sourcecode:: pycon
 | 
						|
 | 
						|
  .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk')
 | 
						|
  >>> 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
 | 
						|
 | 
						|
SVN authentication
 | 
						|
.......................
 | 
						|
 | 
						|
Some uncommon functionality can also be provided as extensions, such as SVN
 | 
						|
authentication:
 | 
						|
 | 
						|
.. sourcecode:: pycon
 | 
						|
 | 
						|
  .. >>> if not py.test.config.option.urlcheck: raise ValueError('skipchunk')
 | 
						|
  >>> auth = py.path.SvnAuth('anonymous', 'user', cache_auth=False,
 | 
						|
  ...             interactive=False)
 | 
						|
  >>> wc.auth = auth
 | 
						|
  >>> wc.update() # this should work
 | 
						|
  >>> path = wc.ensure('thisshouldnotexist.txt')
 | 
						|
  >>> try:
 | 
						|
  ...     path.commit('testing')
 | 
						|
  ... except py.process.cmdexec.Error, e:
 | 
						|
  ...     pass
 | 
						|
  >>> 'authorization failed' in str(e)
 | 
						|
  True
 | 
						|
 | 
						|
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 
 | 
						|
`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 
 | 
						|
(``py/path/gateway/``) aiming at having
 | 
						|
a convenient Remote Path implementation. 
 | 
						|
 | 
						|
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. 
 | 
						|
 | 
						|
.. _`execnet`: execnet.html
 | 
						|
 |