dirutility.ftp.FTP.listdir()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 19
Code Lines 6

Duplication

Lines 19
Ratio 100 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nop 3
dl 19
loc 19
rs 10
c 0
b 0
f 0
1
# Connect to a server via FTP and execute commands
2
import ftplib
3
import os
4
5
6 View Code Duplication
class FTP:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
7
8
    def __init__(self, host, username, password, port=21):
9
        self._session = self.connect(host, port, username, password)
10
11
    def __enter__(self):
12
        return self
13
14
    def __exit__(self, exc_type, exc_val, exc_tb):
15
        self.disconnect()
16
17
    @property
18
    def session(self):
19
        """Return ftplib.FTP object to give exposure to methods."""
20
        return self._session
21
22
    @staticmethod
23
    def connect(host, port, username, password):
24
        """Connect and login to an FTP server and return ftplib.FTP object."""
25
        # Instantiate ftplib client
26
        session = ftplib.FTP()
27
28
        # Connect to host without auth
29
        session.connect(host, port)
30
31
        # Authenticate connection
32
        session.login(username, password)
33
        return session
34
35
    def disconnect(self):
36
        """Send a QUIT command to the server and close the connection (polite way)."""
37
        self.session.quit()
38
        return True
39
40
    def close(self):
41
        """Close the connection unilaterally, FTP instance is unusable after call."""
42
        self.session.close()
43
        return True
44
45
    def put(self, local, remote):
46
        """Upload a local file to a directory on the remote ftp server."""
47
        return self._store_binary(local, remote)
48
49
    def get(self, remote, local=None, keep_dir_structure=False):
50
        """
51
        Download a remote file on the fto sever to a local directory.
52
53
        :param remote: File path of remote source file
54
        :param local: Directory of local destination directory
55
        :param keep_dir_structure: If True, replicates the remote files folder structure
56
        """
57
        if local and os.path.isdir(local):
58
            os.chdir(local)
59
60
        elif keep_dir_structure:
61
            # Replicate the remote files folder structure
62
            for directory in remote.split(os.sep)[:-1]:
63
                if not os.path.isdir(directory):
64
                    os.mkdir(directory)
65
                os.chdir(directory)
66
67
        # Change to the correct directory if remote is a path not just a name
68
        if os.sep in remote:
69
            directory, file_name = remote.rsplit(os.sep, 1)
70
            self.chdir(directory)
71
        else:
72
            file_name = remote
73
74
        # Download the file and get response
75
        response = self._retrieve_binary(file_name)
76
77
        # Rename downloaded files if local is a file_name string
78
        if local and isinstance(local, str):
79
            os.rename(file_name, local)
80
        return response
81
82
    def chdir(self, directory_path, make=False):
83
        """Change directories and optionally make the directory if it doesn't exist."""
84
        if os.sep in directory_path:
85
            for directory in directory_path.split(os.sep):
86
                if make and not self.directory_exists(directory):
87
                    try:
88
                        self.session.mkd(directory)
89
                    except ftplib.error_perm:
90
                        # Directory already exists
91
                        pass
92
                self.session.cwd(directory)
93
        else:
94
            self.session.cwd(directory_path)
95
96
    def listdir(self, directory_path=None, hidden_files=False):
97
        """
98
        Return a list of files and directories in a given directory.
99
100
        :param directory_path: Optional str (defaults to current directory)
101
        :param hidden_files: Include hidden files
102
        :return: Directory listing
103
        """
104
        # Change current directory if a directory path is specified, otherwise use current
105
        if directory_path:
106
            self.chdir(directory_path)
107
108
        # Exclude hidden files
109
        if not hidden_files:
110
            return [path for path in self.session.nlst() if not path.startswith('.')]
111
112
        # Include hidden files
113
        else:
114
            return self.session.nlst()
115
116
    def rename(self, from_name, to_name):
117
        """Rename a file from_name on the server to to_name."""
118
        return self.session.rename(from_name, to_name)
119
120
    def delete(self, file_path):
121
        """Remove the file named filename from the server."""
122
        if os.sep in file_path:
123
            directory, file_name = file_path.rsplit(os.sep, 1)
124
            self.chdir(directory)
125
            return self.session.delete(file_name)
126
127
        else:
128
            return self.session.delete(file_path)
129
130
    def directory_exists(self, directory):
131
        """Check if directory exists (in current location)"""
132
        file_list = []
133
        self.session.retrlines('LIST', file_list.append)
134
        return any(f.split()[-1] == directory and f.upper().startswith('D') for f in file_list)
135
136
    def set_verbosity(self, level):
137
        """Set the instance’s debugging level, controls the amount of debugging output printed."""
138
        self.session.set_debuglevel(level)
139
140
    def _retrieve_binary(self, file_name):
141
        """Retrieve a file in binary transfer mode."""
142
        with open(file_name, 'wb') as f:
143
            return self.session.retrbinary('RETR ' + file_name, f.write)
144
145
    def _store_binary(self, local_path, remote):
146
        """Store a file in binary via ftp."""
147
        # Destination directory
148
        dst_dir = os.path.dirname(remote)
149
150
        # Destination file name
151
        dst_file = os.path.basename(remote)
152
153
        # File upload command
154
        dst_cmd = 'STOR {0}'.format(dst_file)
155
156
        with open(local_path, 'rb') as local_file:
157
            # Change directory if needed
158
            if dst_dir != dst_file:
159
                self.chdir(dst_dir, make=True)
160
161
            # Upload file & return response
162
            return self.session.storbinary(dst_cmd, local_file)
163