Merge lp:~jelmer/brz/medusa into lp:brz

Proposed by Jelmer Vernooij
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/medusa
Merge into: lp:brz
Diff against target: 550 lines (+56/-349)
5 files modified
breezy/tests/ftp_server/__init__.py (+6/-24)
breezy/tests/ftp_server/medusa_based.py (+0/-295)
breezy/tests/ftp_server/pyftpdlib_based.py (+44/-29)
doc/en/release-notes/brz-3.0.txt (+6/-0)
setup.py (+0/-1)
To merge this branch: bzr merge lp:~jelmer/brz/medusa
Reviewer Review Type Date Requested Status
Martin Packman Approve
Review via email: mp+325457@code.launchpad.net

Commit message

Drop support for medusa, support newer versions of pyftpdlib.

Description of the change

Drop support for medusa.

Support newer versions of pyftpdlib.

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote :

Looks good to me.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/tests/ftp_server/__init__.py'
2--- breezy/tests/ftp_server/__init__.py 2017-05-21 18:10:28 +0000
3+++ breezy/tests/ftp_server/__init__.py 2017-06-11 13:38:25 +0000
4@@ -27,27 +27,11 @@
5
6
7 try:
8- from breezy.tests.ftp_server import medusa_based
9- # medusa is bogus starting with python2.6, since we don't support earlier
10- # pythons anymore, it's currently useless. There is hope though that the
11- # unicode bugs get fixed in the future so we leave it disabled until
12- # then. Keeping the framework in place means that only the following line
13- # will need to be changed. The last tests were conducted with medusa-2.0
14- # -- vila 20110607
15- medusa_available = False
16-except ImportError:
17- medusa_available = False
18-
19-
20-try:
21- from breezy.tests.ftp_server import pyftpdlib_based
22- if pyftpdlib_based.pyftplib_version >= (0, 7, 0):
23- pyftpdlib_available = True
24- else:
25- # 0.6.0 breaks SITE CHMOD
26- pyftpdlib_available = False
27+ import pyftpdlib
28 except ImportError:
29 pyftpdlib_available = False
30+else:
31+ pyftpdlib_available = True
32
33
34 class _FTPServerFeature(features.Feature):
35@@ -56,12 +40,11 @@
36 Right now, the only way this is available is if one of the following is
37 installed:
38
39- - 'medusa': http://www.amk.ca/python/code/medusa.html
40 - 'pyftpdlib': http://code.google.com/p/pyftpdlib/
41 """
42
43 def _probe(self):
44- return medusa_available or pyftpdlib_available
45+ return pyftpdlib_available
46
47 def feature_name(self):
48 return 'FTPServer'
49@@ -92,9 +75,8 @@
50 raise tests.UnavailableFeature(FTPServerFeature)
51
52
53-if medusa_available:
54- FTPTestServer = medusa_based.FTPTestServer
55-elif pyftpdlib_available:
56+if pyftpdlib_available:
57+ from . import pyftpdlib_based
58 FTPTestServer = pyftpdlib_based.FTPTestServer
59 else:
60 FTPTestServer = UnavailableFTPTestServer
61
62=== removed file 'breezy/tests/ftp_server/medusa_based.py'
63--- breezy/tests/ftp_server/medusa_based.py 2017-05-22 00:56:52 +0000
64+++ breezy/tests/ftp_server/medusa_based.py 1970-01-01 00:00:00 +0000
65@@ -1,295 +0,0 @@
66-# Copyright (C) 2007-2010 Canonical Ltd
67-#
68-# This program is free software; you can redistribute it and/or modify
69-# it under the terms of the GNU General Public License as published by
70-# the Free Software Foundation; either version 2 of the License, or
71-# (at your option) any later version.
72-#
73-# This program is distributed in the hope that it will be useful,
74-# but WITHOUT ANY WARRANTY; without even the implied warranty of
75-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
76-# GNU General Public License for more details.
77-#
78-# You should have received a copy of the GNU General Public License
79-# along with this program; if not, write to the Free Software
80-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
81-"""
82-FTP test server.
83-
84-Based on medusa: http://www.amk.ca/python/code/medusa.html
85-"""
86-
87-import asyncore
88-import errno
89-import os
90-import select
91-import stat
92-import threading
93-
94-import medusa
95-import medusa.filesys
96-import medusa.ftp_server
97-
98-from ... import (
99- osutils,
100- tests,
101- trace,
102- )
103-from .. import test_server
104-
105-
106-class test_filesystem(medusa.filesys.os_filesystem):
107- """A custom filesystem wrapper to add missing functionalities."""
108-
109- def chmod(self, path, mode):
110- p = self.normalize(self.path_module.join (self.wd, path))
111- return os.chmod(self.translate(p), mode)
112-
113-
114-class test_authorizer(object):
115- """A custom Authorizer object for running the test suite.
116-
117- The reason we cannot use dummy_authorizer, is because it sets the
118- channel to readonly, which we don't always want to do.
119- """
120-
121- def __init__(self, root):
122- self.root = root
123- # If secured_user is set secured_password will be checked
124- self.secured_user = None
125- self.secured_password = None
126-
127- def authorize(self, channel, username, password):
128- """Return (success, reply_string, filesystem)"""
129- channel.persona = -1, -1
130- if username == 'anonymous':
131- channel.read_only = 1
132- else:
133- channel.read_only = 0
134-
135- # Check secured_user if set
136- if (self.secured_user is not None
137- and username == self.secured_user
138- and password != self.secured_password):
139- return 0, 'Password invalid.', None
140- else:
141- return 1, 'OK.', test_filesystem(self.root)
142-
143-
144-class ftp_channel(medusa.ftp_server.ftp_channel):
145- """Customized ftp channel"""
146-
147- def log(self, message):
148- """Redirect logging requests."""
149- trace.mutter('ftp_channel: %s', message)
150-
151- def log_info(self, message, type='info'):
152- """Redirect logging requests."""
153- trace.mutter('ftp_channel %s: %s', type, message)
154-
155- def cmd_rnfr(self, line):
156- """Prepare for renaming a file."""
157- self._renaming = line[1]
158- self.respond('350 Ready for RNTO')
159- # TODO: jam 20060516 in testing, the ftp server seems to
160- # check that the file already exists, or it sends
161- # 550 RNFR command failed
162-
163- def cmd_rnto(self, line):
164- """Rename a file based on the target given.
165-
166- rnto must be called after calling rnfr.
167- """
168- if not self._renaming:
169- self.respond('503 RNFR required first.')
170- pfrom = self.filesystem.translate(self._renaming)
171- self._renaming = None
172- pto = self.filesystem.translate(line[1])
173- if os.path.exists(pto):
174- self.respond('550 RNTO failed: file exists')
175- return
176- try:
177- os.rename(pfrom, pto)
178- except (IOError, OSError) as e:
179- # TODO: jam 20060516 return custom responses based on
180- # why the command failed
181- # (bialix 20070418) str(e) on Python 2.5 @ Windows
182- # sometimes don't provide expected error message;
183- # so we obtain such message via os.strerror()
184- self.respond('550 RNTO failed: %s' % os.strerror(e.errno))
185- except:
186- self.respond('550 RNTO failed')
187- # For a test server, we will go ahead and just die
188- raise
189- else:
190- self.respond('250 Rename successful.')
191-
192- def cmd_size(self, line):
193- """Return the size of a file
194-
195- This is overloaded to help the test suite determine if the
196- target is a directory.
197- """
198- filename = line[1]
199- if not self.filesystem.isfile(filename):
200- if self.filesystem.isdir(filename):
201- self.respond('550 "%s" is a directory' % (filename,))
202- else:
203- self.respond('550 "%s" is not a file' % (filename,))
204- else:
205- self.respond('213 %d'
206- % (self.filesystem.stat(filename)[stat.ST_SIZE]),)
207-
208- def cmd_mkd(self, line):
209- """Create a directory.
210-
211- Overloaded because default implementation does not distinguish
212- *why* it cannot make a directory.
213- """
214- if len (line) != 2:
215- self.command_not_understood(''.join(line))
216- else:
217- path = line[1]
218- try:
219- self.filesystem.mkdir (path)
220- self.respond ('257 MKD command successful.')
221- except (IOError, OSError) as e:
222- # (bialix 20070418) str(e) on Python 2.5 @ Windows
223- # sometimes don't provide expected error message;
224- # so we obtain such message via os.strerror()
225- self.respond ('550 error creating directory: %s' %
226- os.strerror(e.errno))
227- except:
228- self.respond ('550 error creating directory.')
229-
230- def cmd_site(self, line):
231- """Site specific commands."""
232- command, args = line[1].split(' ', 1)
233- if command.lower() == 'chmod':
234- try:
235- mode, path = args.split()
236- mode = int(mode, 8)
237- except ValueError:
238- # We catch both malformed line and malformed mode with the same
239- # ValueError.
240- self.command_not_understood(' '.join(line))
241- return
242- try:
243- # Yes path and mode are reversed
244- self.filesystem.chmod(path, mode)
245- self.respond('200 SITE CHMOD command successful')
246- except AttributeError:
247- # The chmod method is not available in read-only and will raise
248- # AttributeError since a different filesystem is used in that
249- # case
250- self.command_not_authorized(' '.join(line))
251- else:
252- # Another site specific command was requested. We don't know that
253- # one
254- self.command_not_understood(' '.join(line))
255-
256-
257-class ftp_server(medusa.ftp_server.ftp_server):
258- """Customize the behavior of the Medusa ftp_server.
259-
260- There are a few warts on the ftp_server, based on how it expects
261- to be used.
262- """
263- _renaming = None
264- ftp_channel_class = ftp_channel
265-
266- def __init__(self, *args, **kwargs):
267- trace.mutter('Initializing ftp_server: %r, %r', args, kwargs)
268- medusa.ftp_server.ftp_server.__init__(self, *args, **kwargs)
269-
270- def log(self, message):
271- """Redirect logging requests."""
272- trace.mutter('ftp_server: %s', message)
273-
274- def log_info(self, message, type='info'):
275- """Override the asyncore.log_info so we don't stipple the screen."""
276- trace.mutter('ftp_server %s: %s', type, message)
277-
278-
279-class FTPTestServer(test_server.TestServer):
280- """Common code for FTP server facilities."""
281-
282- no_unicode_support = True
283-
284- def __init__(self):
285- self._root = None
286- self._ftp_server = None
287- self._port = None
288- self._async_thread = None
289- # ftp server logs
290- self.logs = []
291-
292- def get_url(self):
293- """Calculate an ftp url to this server."""
294- return 'ftp://foo:bar@localhost:%d/' % (self._port)
295-
296- def get_bogus_url(self):
297- """Return a URL which cannot be connected to."""
298- return 'ftp://127.0.0.1:1'
299-
300- def log(self, message):
301- """This is used by medusa.ftp_server to log connections, etc."""
302- self.logs.append(message)
303-
304- def start_server(self, vfs_server=None):
305- if not (vfs_server is None or isinstance(vfs_server,
306- test_server.LocalURLServer)):
307- raise AssertionError(
308- "FTPServer currently assumes local transport, got %s" % vfs_server)
309- self._root = osutils.getcwd()
310- self._ftp_server = ftp_server(
311- authorizer=test_authorizer(root=self._root),
312- ip='localhost',
313- port=0, # bind to a random port
314- resolver=None,
315- logger_object=self # Use FTPServer.log() for messages
316- )
317- self._port = self._ftp_server.getsockname()[1]
318- # Don't let it loop forever, or handle an infinite number of requests.
319- # In this case it will run for 1000s, or 10000 requests
320- self._async_thread = threading.Thread(
321- target=FTPTestServer._asyncore_loop_ignore_EBADF,
322- kwargs={'timeout':0.1, 'count':10000})
323- if 'threads' in tests.selftest_debug_flags:
324- sys.stderr.write('Thread started: %s\n'
325- % (self._async_thread.ident,))
326- self._async_thread.setDaemon(True)
327- self._async_thread.start()
328-
329- def stop_server(self):
330- self._ftp_server.close()
331- asyncore.close_all()
332- self._async_thread.join()
333- if 'threads' in tests.selftest_debug_flags:
334- sys.stderr.write('Thread joined: %s\n'
335- % (self._async_thread.ident,))
336-
337- @staticmethod
338- def _asyncore_loop_ignore_EBADF(*args, **kwargs):
339- """Ignore EBADF during server shutdown.
340-
341- We close the socket to get the server to shutdown, but this causes
342- select.select() to raise EBADF.
343- """
344- try:
345- asyncore.loop(*args, **kwargs)
346- # FIXME: If we reach that point, we should raise an exception
347- # explaining that the 'count' parameter in setUp is too low or
348- # testers may wonder why their test just sits there waiting for a
349- # server that is already dead. Note that if the tester waits too
350- # long under pdb the server will also die.
351- except select.error as e:
352- if e.args[0] != errno.EBADF:
353- raise
354-
355- def add_user(self, user, password):
356- """Add a user with write access."""
357- authorizer = server = self._ftp_server.authorizer
358- authorizer.secured_user = user
359- authorizer.secured_password = password
360-
361
362=== modified file 'breezy/tests/ftp_server/pyftpdlib_based.py'
363--- breezy/tests/ftp_server/pyftpdlib_based.py 2017-05-22 00:56:52 +0000
364+++ breezy/tests/ftp_server/pyftpdlib_based.py 2017-06-11 13:38:25 +0000
365@@ -20,8 +20,20 @@
366 """
367
368 import errno
369+import logging
370 import os
371-from pyftpdlib import ftpserver
372+import pyftpdlib
373+import sys
374+from pyftpdlib.authorizers import (
375+ AuthorizerError,
376+ DummyAuthorizer,
377+ )
378+from pyftpdlib.filesystems import AbstractedFS
379+from pyftpdlib.handlers import (
380+ FTPHandler,
381+ proto_cmds,
382+ )
383+from pyftpdlib.servers import FTPServer
384 import select
385 import threading
386
387@@ -34,22 +46,31 @@
388 from breezy.tests import test_server
389
390
391+class NullHandler(logging.Handler):
392+
393+ def emit(self, record):
394+ pass
395+
396+# Shut up very verbose pyftpdlib
397+logging.getLogger('pyftpdlib').addHandler(NullHandler())
398+
399+
400 # Convert the pyftplib string version into a tuple to avoid traps in string
401 # comparison.
402-pyftplib_version = tuple(map(int, ftpserver.__ver__.split('.')))
403-
404-
405-class AnonymousWithWriteAccessAuthorizer(ftpserver.DummyAuthorizer):
406+pyftplib_version = tuple(map(int, pyftpdlib.__ver__.split('.')))
407+
408+
409+class AnonymousWithWriteAccessAuthorizer(DummyAuthorizer):
410
411 def _check_permissions(self, username, perm):
412 # Like base implementation but don't warn about write permissions
413 # assigned to anonymous, since that's exactly our purpose.
414 for p in perm:
415 if p not in self.read_perms + self.write_perms:
416- raise ftpserver.AuthorizerError('No such permission "%s"' %p)
417-
418-
419-class BzrConformingFS(ftpserver.AbstractedFS):
420+ raise AuthorizerError('No such permission "%s"' %p)
421+
422+
423+class BzrConformingFS(AbstractedFS):
424
425 def chmod(self, path, mode):
426 return os.chmod(path, mode)
427@@ -59,19 +80,20 @@
428 return [osutils.safe_utf8(s) for s in os.listdir(path)]
429
430 def fs2ftp(self, fspath):
431- p = ftpserver.AbstractedFS.fs2ftp(self, osutils.safe_unicode(fspath))
432+ p = AbstractedFS.fs2ftp(self, osutils.safe_unicode(fspath))
433 return osutils.safe_utf8(p)
434
435 def ftp2fs(self, ftppath):
436 p = osutils.safe_unicode(ftppath)
437- return ftpserver.AbstractedFS.ftp2fs(self, p)
438-
439-class BzrConformingFTPHandler(ftpserver.FTPHandler):
440+ return AbstractedFS.ftp2fs(self, p)
441+
442+
443+class BzrConformingFTPHandler(FTPHandler):
444
445 abstracted_fs = BzrConformingFS
446
447- def __init__(self, conn, server):
448- ftpserver.FTPHandler.__init__(self, conn, server)
449+ def __init__(self, conn, server, ioloop=None):
450+ FTPHandler.__init__(self, conn, server)
451 self.authorizer = server.authorizer
452
453 def ftp_SIZE(self, path):
454@@ -83,7 +105,7 @@
455 self.log('FAIL SIZE "%s". %s.' % (line, why))
456 self.respond("550 %s." %why)
457 else:
458- ftpserver.FTPHandler.ftp_SIZE(self, path)
459+ FTPHandler.ftp_SIZE(self, path)
460
461 def ftp_NLST(self, path):
462 # bzr is overly picky here, but we want to make the test suite pass
463@@ -94,7 +116,7 @@
464 self.log('FAIL NLST "%s". %s.' % (line, why))
465 self.respond("550 %s." %why)
466 else:
467- ftpserver.FTPHandler.ftp_NLST(self, path)
468+ FTPHandler.ftp_NLST(self, path)
469
470 def log_cmd(self, cmd, arg, respcode, respstr):
471 # base class version choke on unicode, the alternative is to just
472@@ -107,12 +129,12 @@
473
474
475 # An empty password is valid, hence the arg is neither mandatory nor forbidden
476-ftpserver.proto_cmds['PASS']['arg'] = None
477+proto_cmds['PASS']['arg'] = None
478
479-class ftp_server(ftpserver.FTPServer):
480+class ftp_server(FTPServer):
481
482 def __init__(self, address, handler, authorizer):
483- ftpserver.FTPServer.__init__(self, address, handler)
484+ FTPServer.__init__(self, address, handler)
485 self.authorizer = authorizer
486 # Worth backporting upstream ?
487 self.addr = self.socket.getsockname()
488@@ -155,13 +177,6 @@
489 authorizer.add_anonymous(self._root, perm='elradfmwM')
490 self._ftp_server = ftp_server(address, BzrConformingFTPHandler,
491 authorizer)
492- # This is hacky as hell, will not work if we need two servers working
493- # at the same time, but that's the best we can do so far...
494- # FIXME: At least log and logline could be overriden in the handler ?
495- # -- vila 20090227
496- ftpserver.log = self.log
497- ftpserver.logline = self.log
498- ftpserver.logerror = self.log
499
500 self._port = self._ftp_server.socket.getsockname()[1]
501 self._ftpd_starting = threading.Lock()
502@@ -196,11 +211,11 @@
503 self._ftpd_starting.release()
504 while self._ftpd_running:
505 try:
506- self._ftp_server.serve_forever(timeout=0.1, count=1)
507+ self._ftp_server.serve_forever(timeout=0.1)
508 except select.error as e:
509 if e.args[0] != errno.EBADF:
510 raise
511- self._ftp_server.close_all(ignore_all=True)
512+ self._ftp_server.close_all()
513
514 def add_user(self, user, password):
515 """Add a user with write access."""
516
517=== modified file 'doc/en/release-notes/brz-3.0.txt'
518--- doc/en/release-notes/brz-3.0.txt 2017-06-11 01:27:46 +0000
519+++ doc/en/release-notes/brz-3.0.txt 2017-06-11 13:38:25 +0000
520@@ -41,6 +41,9 @@
521 URL schemes "http+pycurl://" and "https+pycurl://" has been dropped.
522 (Jelmer Vernooij, #82086, #377389, #122258, #516222, #545776, #1696602)
523
524+ * Support for medusa for FTP tests has been dropped, only
525+ pyftpdlib is now supported. (Jelmer Vernooij)
526+
527 New Features
528 ************
529
530@@ -135,5 +138,8 @@
531 which caused ``output_encoding = iso-8859-1`` to be added to the
532 users' bazaar.conf. (Jelmer Vernooij)
533
534+ * Newer versions of ``pyftpdlib`` are now supported for running FTP tests.
535+ (Jelmer Vernooij)
536+
537 ..
538 vim: tw=74 ft=rst ff=unix
539
540=== modified file 'setup.py'
541--- setup.py 2017-06-07 00:34:37 +0000
542+++ setup.py 2017-06-11 13:38:25 +0000
543@@ -587,7 +587,6 @@
544 excludes = """Tkinter psyco ElementPath r_hmac
545 ImaginaryModule cElementTree elementtree.ElementTree
546 Crypto.PublicKey._fastmath
547- medusa medusa.filesys medusa.ftp_server
548 tools
549 resource validate""".split()
550 dll_excludes = []

Subscribers

People subscribed via source and target branches