source: ftputil/session.py @ 1713:f146a1ea66aa

Last change on this file since 1713:f146a1ea66aa was 1713:f146a1ea66aa, checked in by Stefan Schwarzer <sschwarzer@…>, 9 months ago
Remove `__future__` imports With the switch to Python 3.x-only, the `__future__` imports are no longer needed. Update copyright years along with the `__future__` import removal.
File size: 4.0 KB
Line 
1# Copyright (C) 2014-2018, Stefan Schwarzer <sschwarzer@sschwarzer.net>
2# and ftputil contributors (see `doc/contributors.txt`)
3# See the file LICENSE for licensing terms.
4
5"""
6Session factory factory (the two "factory" are intential :-) )
7for ftputil.
8"""
9
10import ftplib
11
12import ftputil.tool
13
14try:
15    import M2Crypto
16    import M2Crypto.ftpslib
17except ImportError:
18    M2Crypto = None
19
20
21__all__ = ["session_factory"]
22
23
24# In a way, it would be appropriate to call this function
25# `session_factory_factory`, but that's cumbersome to use. Think of
26# the function returning a session factory and the shorter name should
27# be fine.
28def session_factory(base_class=ftplib.FTP, port=21, use_passive_mode=None,
29                    encrypt_data_channel=True, debug_level=None):
30    """
31    Create and return a session factory according to the keyword
32    arguments.
33
34    base_class: Base class to use for the session class (e. g.
35    `ftplib.FTP_TLS` or `M2Crypto.ftpslib.FTP_TLS`, default is
36    `ftplib.FTP`).
37
38    port: Port number (integer) for the command channel (default 21).
39    If you don't know what "command channel" means, use the default or
40    use what the provider gave you as "the FTP port".
41
42    use_passive_mode: If `True`, explicitly use passive mode. If
43    `False`, explicitly don't use passive mode. If `None` (default),
44    let the `base_class` decide whether it wants to use active or
45    passive mode.
46
47    encrypt_data_channel: If `True` (the default), call the `prot_p`
48    method of the base class if it has the method. If `False` or
49    `None` (`None` is the default), don't call the method.
50
51    debug_level: Debug level (integer) to be set on a session
52    instance. The default is `None`, meaning no debugging output.
53
54    This function should work for the base classes `ftplib.FTP`,
55    `ftplib.FTP_TLS` and `M2Crypto.ftpslib.FTP_TLS` with TLS security.
56    Other base classes should work if they use the same API as
57    `ftplib.FTP`.
58
59    Usage example:
60
61      my_session_factory = session_factory(
62                             base_class=M2Crypto.ftpslib.FTP_TLS,
63                             use_passive_mode=True,
64                             encrypt_data_channel=True)
65      with ftputil.FTPHost(host, user, password,
66                           session_factory=my_session_factory) as host:
67        ...
68    """
69    class Session(base_class):
70        """Session factory class created by `session_factory`."""
71
72        def __init__(self, host, user, password):
73            # Don't use `super` in case `base_class` isn't a new-style
74            # class (e. g. `ftplib.FTP` in Python 2).
75            base_class.__init__(self)
76            self.connect(host, port)
77            if self._use_m2crypto_ftpslib():
78                self.auth_tls()
79                self._fix_socket()
80            if debug_level is not None:
81                self.set_debuglevel(debug_level)
82            self.login(user, password)
83            # `set_pasv` can be called with `True` (causing passive
84            # mode) or `False` (causing active mode).
85            if use_passive_mode is not None:
86                self.set_pasv(use_passive_mode)
87            if encrypt_data_channel and hasattr(base_class, "prot_p"):
88                self.prot_p()
89
90        def _use_m2crypto_ftpslib(self):
91            """
92            Return `True` if the base class to use is
93            `M2Crypto.ftpslib.FTP_TLS`, else return `False`.
94            """
95            return (M2Crypto is not None and
96                    issubclass(base_class, M2Crypto.ftpslib.FTP_TLS))
97
98        def _fix_socket(self):
99            """
100            Change the socket object so that arguments to `sendall`
101            are converted to byte strings before being used.
102
103            See the ftputil ticket #78 for details:
104            http://ftputil.sschwarzer.net/trac/ticket/78
105            """
106            original_sendall = self.sock.sendall
107            # Bound method, therefore no `self` argument.
108            def sendall(data):
109                data = ftputil.tool.as_bytes(data)
110                return original_sendall(data)
111            self.sock.sendall = sendall
112
113    return Session
Note: See TracBrowser for help on using the repository browser.