source: ftputil/session_adapter.py @ 1610:618b30604061

Last change on this file since 1610:618b30604061 was 1610:618b30604061, checked in by Stefan Schwarzer <sschwarzer@…>, 4 years ago
Remove function `adapted_session`; apply `SessionAdapter` directly Don't keep the helper function `adapted_session` for applying it just once. Instead, when adapting an FTP session for Python 2 in `ftputil.host._make_session`, wrap it in a `SessionAdapter` object directly. This is like the wrapping in `ftputil.file.FTPFile._open` where we have the Python version check directly in the method.
File size: 3.5 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)
Note: See TracBrowser for help on using the repository browser.