source: ftputil/session_adapter.py @ 1604:f22e1a2a3313

Last change on this file since 1604:f22e1a2a3313 was 1604:f22e1a2a3313, checked in by Stefan Schwarzer <sschwarzer@…>, 6 years ago
Add `__future__` import for unicode literals Although none of the session adapter code uses strings for the "real" implementation, make sure that docstrings are unicode.
File size: 3.9 KB
Line 
1# Copyright (C) 2015, Stefan Schwarzer <sschwarzer@sschwarzer.net>
2# and ftputil contributors (see `doc/contributors.txt`)
3# See the file LICENSE for licensing terms.
4
5"""
6Session adapter for `ftplib.FTP`-compatible session factories
7to make them usable with ftputil under Python 2.
8
9`ftplib.FTP` under Python 2 doesn't work with unicode strings
10that contain non-ASCII characters (see ticket #100). Since ftputil
11converts string arguments to unicode strings as soon as possible,
12this even affects calls to `ftputil.FTPHost` methods when the string
13arguments are byte strings.
14
15ftputil client code
16          V
17          V
18`ftputil.FTPHost` methods
19          V
20          V
21convert byte strings to unicode (inside `FTPHost` methods)
22          V
23          V
24session adapter converts from unicode to byte strings
25          V
26          V
27`ftplib.FTP` (or other session) methods
28
29You may wonder why we convert byte strings to unicode strings in
30`ftputil.FTPHost` and unicode strings back to byte strings in the
31adapter. To make the string handling in ftputil consistent for Python
322 and 3, ftputil tries to work everywhere with the same string type.
33Because the focus of ftputil is on modern Python (i. e. Python 3),
34this universal string type is unicode. Indeed the string arguments
35for `ftplib.FTP` under Python 3 are all unicode strings.
36
37Having different code for Python 2 and Python 3 all over in ftputil
38would be bad for maintainability. (ftputil is complicated enough as it
39is.) Therefore, this adapter is the only place to deal with the
40preferred string type of `ftplib` under Python 2 vs. Python 3.
41"""
42
43from __future__ import unicode_literals
44
45import ftputil.compat
46import ftputil.tool
47
48
49# Shouldn't be used by ftputil client code
50__all__ = []
51
52
53# Shortcut
54as_bytes = ftputil.tool.as_bytes
55
56
57# We only have to adapt the methods that are directly called by
58# ftputil and only those that take string arguments.
59#
60# Keep the call signature of each adaptor method the same as the
61# signature of the adapted method so that code that introspects
62# the signature of the method still works.
63
64class SessionAdapter(object):
65
66    def __init__(self, session):
67        self._session = session
68
69    def voidcmd(self, cmd):
70        cmd = as_bytes(cmd)
71        return self._session.voidcmd(cmd)
72
73    def transfercmd(self, cmd, rest=None):
74        cmd = as_bytes(cmd)
75        return self._session.transfercmd(cmd, rest)
76
77    def dir(self, *args):
78        # This is somewhat tricky, since some of the args may not
79        # be strings. The last argument may be a callback.
80        args = list(args)
81        for index, arg in enumerate(args):
82            # Replace only unicode strings with a corresponding
83            # byte string.
84            if isinstance(arg, ftputil.compat.unicode_type):
85                args[index] = as_bytes(arg)
86        return self._session.dir(*args)
87
88    def rename(self, fromname, toname):
89        fromname = as_bytes(fromname)
90        toname = as_bytes(toname)
91        return self._session.rename(fromname, toname)
92
93    def delete(self, filename):
94        filename = as_bytes(filename)
95        return self._session.delete(filename)
96
97    def cwd(self, dirname):
98        dirname = as_bytes(dirname)
99        return self._session.cwd(dirname)
100
101    def mkd(self, dirname):
102        dirname = as_bytes(dirname)
103        return self._session.mkd(dirname)
104
105    def rmd(self, dirname):
106        dirname = as_bytes(dirname)
107        return self._session.rmd(dirname)
108
109    # Dispatch to session itself for methods that don't need string
110    # conversions.
111    def __getattr__(self, name):
112        return getattr(self._session, name)
113
114
115def adapted_session(session):
116    """
117    Return an adapted session that will work with Python 2's
118    `ftplib.FTP` and compatible sessions.
119
120    Under Python 3 return the passed session itself.
121    """
122    if ftputil.compat.python_version == 2:
123        return SessionAdapter(session)
124    else:
125        return session
Note: See TracBrowser for help on using the repository browser.