File reading across network (windows)

Duncan Booth duncan.booth at invalid.invalid
Tue Aug 12 10:47:12 CEST 2008


"Diez B. Roggisch" <deets at nospam.web.de> wrote:

>> A search suggested that the form open(r"\\server\folder\folder"), but
>> I tried many combinations guessing what it wants for that path in my
>> case (r"\\WORKGROUP\VISTA", "\\VISTA\PUBLIC", etc), and none have
>> worked so far.

The combination you want is \\server\sharename\folder\filename, so if 
the Vista machine is called VISTA and has a folder shared as PUBLIC then 
"\\VISTA\PUBLIC" might work. But several things can go wrong with this.

> 
> You need to create network shares in windows first. Once these are 
> established, *all* programs using file-IO - including python - can 
> access files.
> 
It isn't absolutely essential to create a network share first: most 
windows apis, including those used by Python, will be very happy with a 
UNC path:

>>> open(r'\\10.10.25.121\rootc\default.tag', 'r').read()
'\tTAGS\tUNSORTED\n'

The first problem you need to address is whether or not the two systems 
know each other's names. This can be problematic: that's why I used an 
IP address in the example I gave. If you don't have a domain controller 
on the  network then the machines which are on the network use a complex 
system of voting to decide which will be the 'browse master', but they 
don't always agree on the outcome :^)

The second problem is that if you don't establish a connection first, 
you cannot specify a username and password when opening the file. That 
means you get whatever the default is. Again not usually a problem if 
your machines are in a domain, but in a workgroup it can be messy.

So while it is not essential, it is probably easier to establish a 
connection. Of course that can be done fairly easily from within 
Pythonusing win32api.NetUseAdd, ctypes, or even os.system("NET USE 
\\server\share /user:yourname pa5sW0rc!")

Note that you don't need to assign a drive letter when creating the 
connection, just get the authentication over with and then you can use a 
UNC name for the the actual file access.

----- netuse.py -------
from __future__ import with_statement
import ctypes
from ctypes import *
from ctypes.wintypes import *
import getpass
import os

def _stdcall(dllname, restype, funcname, *argtypes):
    # a decorator for a generator.
    # The decorator loads the specified dll, retrieves the
    # function with the specified name, set its restype and argtypes,
    # it then invokes the generator which must yield twice: the first
    # time it should yield the argument tuple to be passed to the dll
    # function (and the yield returns the result of the call).
    # It should then yield the result to be returned from the
    # function call.
    def decorate(func):
        api = getattr(WinDLL(dllname), funcname)
        api.restype = restype
        api.argtypes = argtypes

        def decorated(*args, **kw):
            iterator = func(*args, **kw)
            nargs = iterator.next()
            if not isinstance(nargs, tuple):
                nargs = (nargs,)
            try:
                res = api(*nargs)
            except Exception, e:
                return iterator.throw(e)
            return iterator.send(res)
        return decorated
    return decorate

# Structure for NetUseAdd:
class USE_INFO_2(Structure):
    _fields_ = [("local", LPWSTR),
        ("remote", LPWSTR),
        ("password", LPWSTR),
        ("status", DWORD),
        ("asg_type", DWORD),
        ("refcount", DWORD),
        ("usecount", DWORD),
        ("username", LPWSTR),
        ("domain", LPWSTR)]

@_stdcall("netapi32", c_int, "NetUseAdd", LPWSTR, DWORD, POINTER(USE_INFO_2), POINTER(DWORD))
def NetUseAdd(local, remote, password, asg_type=0, username=None, domainname=None):
    """Add a network connection. Returns 0 if successful"""
    useinfo = USE_INFO_2(local, remote, password, 0, 0, 0, 0, username, domainname)
    ParmError = DWORD()
    res = yield(None, 2, useinfo, ParmError)
    if res != 0:
        raise RuntimeError("Could not create connection", res)
    yield res

@_stdcall("netapi32", c_int, "NetUseDel", LPWSTR, LPWSTR, DWORD)
def NetUseDel(usename, forcecond=0):
    res = yield(None, usename, forcecond)
    if res != 0:
        raise RuntimeError("Could not delete connection", res)
    yield res

if __name__=='__main__':
    username = "ZZZZZZ"
    password = getpass.getpass("Password:")
    sharename = r"\\10.10.25.121\rootc"
    filename = "default.tag"
    try:
        with open(os.path.join(sharename, filename), "r") as f:
            print f.read()
    except IOError, e:
        print "without net use, got", e
    else:
        raise RuntimeError("We connected without doing the net use!")

    NetUseAdd(None, sharename, password, username=username)
    with open(os.path.join(sharename, "default.tag"), "r") as f:
        print f.read()
    NetUseDel(sharename)

-----------------------

-- 
Duncan Booth http://kupuguy.blogspot.com



More information about the Python-list mailing list