← Back to team overview

spv-dev team mailing list archive

lp:~claude-bolduc/slidepresenterview/refactor-pres-design-claude into lp:~bouf10/slidepresenterview/refactor-pres-design

 

C. Bolduc has proposed merging lp:~claude-bolduc/slidepresenterview/refactor-pres-design-claude into lp:~bouf10/slidepresenterview/refactor-pres-design.

Requested reviews:
  SlidePresenterView Development Team (spv-dev)


Refactoring of the Uri class. It now separates logic for local file URIs and general URIs.
Also added factories to help create the right URIs.
-- 
https://code.launchpad.net/~claude-bolduc/slidepresenterview/refactor-pres-design-claude/+merge/26560
Your team SlidePresenterView Development Team is requested to review the proposed merge of lp:~claude-bolduc/slidepresenterview/refactor-pres-design-claude into lp:~bouf10/slidepresenterview/refactor-pres-design.
=== modified file 'slidepresenterview/presentations.py'
--- slidepresenterview/presentations.py	2010-05-09 18:15:55 +0000
+++ slidepresenterview/presentations.py	2010-06-02 05:43:25 +0000
@@ -27,8 +27,6 @@
 ##########
 # Imports
 #####
-import os
-
 from PyQt4 import QtGui
 
 from slidepresenterview.utils.uri import Uri
@@ -50,13 +48,7 @@
     
     
     def is_presenting(self, other_uri):
-        if other_uri.scheme != "file":
-            return self.uri == other_uri
-        
-        try:
-            return os.path.samefile(self.uri.path, other_uri.path)
-        except OSError: # File doesn't exist. Use a string comparison.
-            return self.uri.path == other_uri.path
+        return self.uri == other_uri
 
 
 class EmptyPresentaton(Presentation):

=== modified file 'slidepresenterview/session.py'
--- slidepresenterview/session.py	2010-05-09 18:15:55 +0000
+++ slidepresenterview/session.py	2010-06-02 05:43:25 +0000
@@ -4,6 +4,7 @@
 # https://launchpad.net/slidepresenterview
 #
 # Copyright (C) 2010 Felix-Antoine Bourbonnais <bouf10pub _AT@. rubico.info>
+# Copyright (C) 2010 Claude Bolduc <claude.bolduc _AT@. gmail.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -22,7 +23,7 @@
 See L{Session}
 """
 
-from slidepresenterview.utils.uri import file_path_to_uri
+from slidepresenterview.utils.uri import create_uri_from_file_path
 from slidepresenterview.presentations import FakeFilePresentation
 from slidepresenterview.presentations import EmptyPresentaton
 
@@ -65,7 +66,7 @@
         @param file_path: The presentation local file
         @type file_name:  Unicode string (Python) 
         """
-        file_uri = file_path_to_uri(file_path)
+        file_uri = create_uri_from_file_path(file_path)
         if not self.current_presentation.is_presenting(file_uri):
             self.current_presentation = FakeFilePresentation(file_uri)
             self._notify_presentation_opened()

=== modified file 'slidepresenterview/tests/unit/test_session.py'
--- slidepresenterview/tests/unit/test_session.py	2010-05-09 18:15:55 +0000
+++ slidepresenterview/tests/unit/test_session.py	2010-06-02 05:43:25 +0000
@@ -4,6 +4,7 @@
 # https://launchpad.net/slidepresenterview
 #
 # Copyright (C) 2009 F.-A. Bourbonnais <bouf10pub _AT@. rubico.info>
+# Copyright (C) 2010 Claude Bolduc <claude.bolduc _AT@. gmail.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -26,7 +27,7 @@
 
 from mock import Mock
 
-from slidepresenterview.utils.uri import file_path_to_uri
+from slidepresenterview.utils.uri import create_uri_from_file_path
 
 from slidepresenterview.session import Session
 from slidepresenterview.session import SessionObserver
@@ -115,7 +116,8 @@
         pres = self.session.current_presentation
         
         self.assertFalse(self._is_default_presentation())
-        self.assertTrue(pres.is_presenting(file_path_to_uri('mock.pdf')))
+        self.assertTrue(pres.is_presenting(
+                                        create_uri_from_file_path('mock.pdf')))
         self.assertTrue(self._has_notified_opening_once(pres))
     
     
@@ -149,7 +151,7 @@
         
         self.assertNotEquals(self.pres_before, pres_after)
         self.assertTrue(pres_after.is_presenting(
-                                        file_path_to_uri('mock_other.pdf')))
+                                create_uri_from_file_path('mock_other.pdf')))
         self.assertTrue(self._has_notified_opening_once(pres_after))
     
     

=== modified file 'slidepresenterview/tests/unit/utils/tests_uri.py'
--- slidepresenterview/tests/unit/utils/tests_uri.py	2010-05-17 04:37:32 +0000
+++ slidepresenterview/tests/unit/utils/tests_uri.py	2010-06-02 05:43:25 +0000
@@ -4,6 +4,7 @@
 # https://launchpad.net/slidepresenterview
 #
 # Copyright (C) 2010 Felix-Antoine Bourbonnais <bouf10pub _AT@. rubico.info>
+# Copyright (C) 2010 Claude Bolduc <claude.bolduc _AT@. gmail.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -22,12 +23,16 @@
 Tests for the L{slidepresenterview.utils.uri} module.
 """
 
+import os
+from os.path import normpath as os_path_normpath
+from os.path import splitdrive as os_path_splitdrive
 import unittest
 
 from mock import patch, Mock
 
-from slidepresenterview.utils.uri import file_path_to_uri
-from slidepresenterview.utils.uri import Uri
+from slidepresenterview.utils.uri import create_uri_from_uri_string
+from slidepresenterview.utils.uri import create_uri_from_file_path
+from slidepresenterview.utils.uri import Uri, LocalFileUri
 
 
 import sys
@@ -37,14 +42,126 @@
     _encoding = 'UTF-8'
 
 
-def _fake_abspath(path):
+# Helper methods to create Mocks for paths in different OSes.
+
+def _fake_os_path_unix():
+    module = Mock(spec=os.path)
+    module.abspath = Mock(side_effect=_fake_abspath_unix)
+    module.expanduser = Mock(side_effect=_fake_expanduser_unix)
+    module.expandvars = Mock(side_effect=_fake_expandvars_unix)
+    module.normpath = Mock(side_effect=_fake_normpath_unix)
+    module.normcase = Mock(side_effect=_fake_normcase_unix)
+    module.samefile = Mock(side_effect=_fake_samefile_unix)
+    module.splitdrive = Mock(side_effect=_fake_splitdrive_unix)
+    return module
+
+
+def _fake_abspath_unix(path):
     if len(path) == 0 or path[0] != '/':
         return '/root/%s' % path
     return path
 
 
-
-class TestFilePath_UnixToUri(unittest.TestCase): #pylint: disable-msg=R0904
+def _fake_expanduser_unix(path):
+    if len(path) > 0 and path[0] == '~':
+        return '/home/user/%s' % path[1:]
+    return path
+
+
+def _fake_expandvars_unix(path):
+    path = path.replace('$USER','user')
+    return path
+
+
+def _fake_normpath_unix(path):
+    return os_path_normpath(path)
+
+
+def _fake_normcase_unix(path):
+    return path
+
+
+def _fake_samefile_unix(path, other_path):
+    return (other_path != 'not_exists.pdf' 
+            and other_path != u"/path/a e/fé.pdf")
+
+
+def _fake_splitdrive_unix(path):
+    return os_path_splitdrive(path)
+
+
+def _fake_os_path_windows():
+    module = Mock(spec=os.path)
+    module.abspath = Mock(side_effect=_fake_abspath_windows)
+    module.expanduser = Mock(side_effect=_fake_expanduser_windows)
+    module.expandvars = Mock(side_effect=_fake_expandvars_windows)
+    module.normpath = Mock(side_effect=_fake_normpath_windows)
+    module.normcase = Mock(side_effect=_fake_normcase_windows)
+    module.splitdrive = Mock(side_effect=_fake_splitdrive_windows)
+    return module
+
+
+def _fake_abspath_windows(path):
+    drive, _ = os.path.splitdrive(path)
+    if drive == '':
+        return 'c:/documents and settings/%s' % path
+    return path
+
+
+def _fake_expanduser_windows(path):
+    if len(path) > 0 and path[0] == '~':
+        return 'c:/documents and settings/user%s' % path[1:]
+    return path
+
+
+def _fake_expandvars_windows(path):
+    path = path.replace('%UserProfile%','c:/documents and settings/user')
+    return path
+
+
+def _fake_normpath_windows(path):
+    path = path.replace('\\', '/')
+    return os_path_normpath(path)
+
+
+def _fake_normcase_windows(path):
+    return path.lower()
+
+
+def _fake_splitdrive_windows(path):
+    return os_path_splitdrive(path)
+
+
+#pylint: disable-msg=R0904
+class TestCreateUriFromUriString(unittest.TestCase):
+    
+    def setUp(self):
+        self.local_file_uri_win = u"file:///c:/path/a é/fé.pdf"
+        self.network_file_uri = u"file://localhost/path/fé.pdf"
+        self.http_uri = u"http://éxample.com/patéh?a=é";
+        
+        
+    @patch('os.path', _fake_os_path_windows())
+    def test_should_create_LocalFileUri_when_receiving_local_file_uri_string(
+                                                                         self):
+        uri = create_uri_from_uri_string(self.local_file_uri_win)
+        self.assertTrue(isinstance(uri, LocalFileUri))
+        
+        
+    def test_should_create_Uri_when_receiving_network_file_uri_string(self):
+        uri = create_uri_from_uri_string(self.network_file_uri)
+        self.assertFalse(isinstance(uri, LocalFileUri))
+        self.assertTrue(isinstance(uri, Uri))
+
+
+    def test_should_create_Uri_when_receiving_http_uri_string(self):
+        uri = create_uri_from_uri_string(self.http_uri)
+        self.assertFalse(isinstance(uri, LocalFileUri))
+        self.assertTrue(isinstance(uri, Uri))
+
+
+#pylint: disable-msg=R0904
+class TestCreateUriFromFilePathUnix(unittest.TestCase): 
     
     def setUp(self):
         self.path_filename = 'file.pdf'
@@ -52,51 +169,147 @@
         self.path_abs = '/complete/path/to/file.pdf'
         self.path_unicode = u'sé/filé.pdf'
         self.path_not_norm = '//dir/../dir2/./a//file.pdf'
+        self.path_home = '~/file.pdf'
+        self.path_var = '/home/$USER/file.pdf' 
         
     
-    @patch('os.path.abspath', Mock(side_effect=_fake_abspath))
+    @patch('os.path', _fake_os_path_unix())
     def test_empty_path_should_be_the_current_dir(self):
-        uri = file_path_to_uri('')
+        uri = create_uri_from_file_path('')
         self.assertEquals('file:///root', uri.uri)    
+
         
-    @patch('os.path.abspath', Mock(side_effect=_fake_abspath))
-    def should_procude_a_valid_uri(self):
-        uri = file_path_to_uri(self.path_abs)
+    @patch('os.path', _fake_os_path_unix())
+    def should_produce_a_valid_uri(self):
+        uri = create_uri_from_file_path(self.path_abs)
         self.assertEquals('file:///complete/path/to/file.pdf', uri.uri)
         
         
-    @patch('os.path.abspath', Mock(side_effect=_fake_abspath))
+    @patch('os.path', _fake_os_path_unix())
     def test_filename_should_return_an_absolute_path(self):
-        uri = file_path_to_uri(self.path_filename)
+        uri = create_uri_from_file_path(self.path_filename)
         self.assertEquals('/root/file.pdf', uri.path)
         
         
-    @patch('os.path.abspath', Mock(side_effect=_fake_abspath))
+    @patch('os.path', _fake_os_path_unix())
     def test_relative_path_should_return_an_absolute_path(self):
-        uri = file_path_to_uri(self.path_rel)
+        uri = create_uri_from_file_path(self.path_rel)
         self.assertEquals('/root/sub/file.pdf', uri.path)
         
     
-    @patch('os.path.abspath', Mock(side_effect=_fake_abspath))
-    def should_be_normalised(self):
-        uri = file_path_to_uri(self.path_not_norm)
+    @patch('os.path', _fake_os_path_unix())
+    def should_be_normalized(self):
+        uri = create_uri_from_file_path(self.path_not_norm)
         self.assertEquals('//dir2/a/file.pdf', uri.path)
         
+        
+    @patch('os.path', _fake_os_path_unix())
+    def should_be_expanded_for_user(self):
+        uri = create_uri_from_file_path(self.path_home)
+        self.assertEquals('/home/user/file.pdf', uri.path)
+        
+        
+    @patch('os.path', _fake_os_path_unix())
+    def should_be_expanded_for_environment_variables(self):
+        uri = create_uri_from_file_path(self.path_var)
+        self.assertEquals('/home/user/file.pdf', uri.path)
+        
     
-    @patch('os.path.abspath', Mock(side_effect=_fake_abspath))
+    @patch('os.path', _fake_os_path_unix())
     def should_accept_unicode(self):
-        uri = file_path_to_uri(self.path_unicode)
+        uri = create_uri_from_file_path(self.path_unicode)
         self.assertEquals(u'/root/sé/filé.pdf', uri.path)
         
         
-class TestFilePath_WinToUri(unittest.TestCase):
+    @patch('os.path', _fake_os_path_unix())
+    def should_have_correct_original_path(self):
+        uri = create_uri_from_file_path(self.path_rel)
+        self.assertEquals('sub/file.pdf', uri.original_path)
+        
+        
+class TestCreateUriFromFilePathWindows(unittest.TestCase):
     
     def setUp(self):
-        self.path_win = u'c:/document and settings/aée/My Documents/file.pdf'
-        self.path_win_not_norm = u'c:\document and settings/a\\ée//file.pdf'
-        
-    def should_todo(self):
-        self.fail("Not implemented.")
+        self.path_filename = 'file.pdf'
+        self.path_rel = 'sub/file.pdf'
+        self.path_abs = u'c:/documents and settings/aée/My Documents/file.pdf'
+        self.path_not_norm = u'C:\\Documents and Settings/a\\..\\Ée//file.pdf'
+        self.path_home = '~\\file.pdf'
+        self.path_var = '%UserProfile%\\file.pdf'
+        self.path_short = 'c:/docum~1'
+        
+        
+    @patch('os.path', _fake_os_path_windows())
+    def test_empty_path_should_be_the_current_dir(self):
+        uri = create_uri_from_file_path('')
+        self.assertEquals('file:///c:/documents and settings', uri.uri)    
+
+        
+    @patch('os.path', _fake_os_path_windows())
+    def should_produce_a_valid_uri(self):
+        uri = create_uri_from_file_path(self.path_abs)
+        self.assertEquals(
+          u'file:///c:/documents and settings/aée/my documents/file.pdf', 
+          uri.uri)
+        
+        
+    @patch('os.path', _fake_os_path_windows())
+    def test_path_should_return_a_path_without_slash_at_first_position(self):
+        uri = create_uri_from_file_path(self.path_abs)
+        self.assertEquals(
+          u'c:/documents and settings/aée/my documents/file.pdf', 
+          uri.path)
+        
+        
+    @patch('os.path', _fake_os_path_windows())
+    def test_filename_should_return_an_absolute_path(self):
+        uri = create_uri_from_file_path(self.path_filename)
+        self.assertEquals('c:/documents and settings/file.pdf', uri.path)
+        
+        
+    @patch('os.path', _fake_os_path_windows())
+    def test_relative_path_should_return_an_absolute_path(self):
+        uri = create_uri_from_file_path(self.path_rel)
+        self.assertEquals('c:/documents and settings/sub/file.pdf', uri.path)
+        
+    
+    @patch('os.path', _fake_os_path_windows())
+    def should_be_normalized(self):
+        uri = create_uri_from_file_path(self.path_not_norm)
+        self.assertEquals(u"c:/documents and settings/ée/file.pdf", 
+                          uri.path)
+        
+        
+    @patch('os.path', _fake_os_path_windows())
+    def should_be_expanded_for_user(self):
+        uri = create_uri_from_file_path(self.path_home)
+        self.assertEquals('c:/documents and settings/user/file.pdf', uri.path)
+        
+        
+    @patch('os.path', _fake_os_path_windows())
+    def should_be_expanded_for_environment_variables(self):
+        uri = create_uri_from_file_path(self.path_var)
+        self.assertEquals('c:/documents and settings/user/file.pdf', uri.path)
+
+
+    @patch('os.path', _fake_os_path_windows())
+    def should_not_expand_short_names(self):
+        uri = create_uri_from_file_path(self.path_short)
+        self.assertEquals('c:/docum~1', uri.path)
+        
+    
+    @patch('os.path', _fake_os_path_windows())
+    def should_accept_unicode(self):
+        uri = create_uri_from_file_path(self.path_abs)
+        self.assertEquals(
+          u'file:///c:/documents and settings/aée/my documents/file.pdf', 
+          uri.uri)
+        
+        
+    @patch('os.path', _fake_os_path_windows())
+    def should_have_correct_original_path(self):
+        uri = create_uri_from_file_path(self.path_rel)
+        self.assertEquals('sub/file.pdf', uri.original_path)
 
 
 class TestUri_GivenInvalidUri(unittest.TestCase):
@@ -112,6 +325,39 @@
         self.assertRaises(ValueError, Uri, 'http://')
 
 
+class TestLocalFileUri_GivenInvalidFileUri(unittest.TestCase):
+    
+    @patch('os.path', _fake_os_path_unix())
+    def test_empty_should_raise_valueerror(self):
+        self.assertRaises(ValueError, LocalFileUri, '')
+    
+    @patch('os.path', _fake_os_path_unix())
+    def test_no_scheme_should_raise_valueerror(self):
+        self.assertRaises(ValueError, LocalFileUri, '/path')
+    
+    @patch('os.path', _fake_os_path_unix())
+    def test_no_path_should_raise_valueerror(self):
+        self.assertRaises(ValueError, LocalFileUri, 'file:')
+        self.assertRaises(ValueError, LocalFileUri, 'file://')
+    
+    @patch('os.path', _fake_os_path_windows())    
+    def test_a_netloc_should_raise_valueerror(self):
+        self.assertRaises(ValueError, LocalFileUri, 
+                          'file://localhost/c:/file.pdf')
+
+    @patch('os.path', _fake_os_path_windows())
+    def test_incorrect_original_path_should_raise_valueerror_windows(self):
+        self.assertRaises(ValueError, LocalFileUri, 
+                          'file://c:/documents and settings/file.pdf', 
+                          'not_exists.pdf')
+        
+    @patch('os.path', _fake_os_path_unix())
+    def test_incorrect_original_path_should_raise_valueerror_unix(self):
+        self.assertRaises(ValueError, LocalFileUri, 
+                          'file:///root/file.pdf', 
+                          'not_exists.pdf')
+
+
 class TestUri_GivenAsciiUri(unittest.TestCase):
     
     def setUp(self):
@@ -173,57 +419,82 @@
         self.assertTrue(isinstance(unicode(self.uri), unicode))
       
               
-class TestUri_GivenFileScheme(unittest.TestCase): 
+class TestLocalFileUri_GivenUnixStylePath(unittest.TestCase): 
     
     def setUp(self):
         self.uri_unicode = u"file:///path/to/résource?a=é"
-        self.uri = Uri(self.uri_unicode)
-        
-        
+        self.uri = LocalFileUri(self.uri_unicode)
+        
+    
+    @patch('os.path', _fake_os_path_unix())    
     def should_have_file_as_scheme(self):
         self.assertEquals("file", self.uri.scheme)
     
     
+    @patch('os.path', _fake_os_path_unix())
     def should_have_no_netloc(self):
         self.assertEquals("", self.uri.netloc)
         
     
+    @patch('os.path', _fake_os_path_unix())
     def should_have_no_query(self):
         self.assertEquals("", self.uri.query)
+        
+    
+    @patch('os.path', _fake_os_path_unix())
+    def should_have_a_slash_at_first_position_in_path(self):
+        self.assertEquals(u"/path/to/résource?a=é", self.uri.path)
+        
+        
+    @patch('os.path', _fake_os_path_unix())
+    def should_have_same_original_path_as_path(self):
+        self.assertEquals(self.uri.path, self.uri.original_path)
+        
+        
+    @patch('os.path', _fake_os_path_unix())
+    def should_have_the_same_full_uri(self):
+        self.assertEquals(self.uri_unicode, self.uri.uri)
       
       
 #pylint: disable-msg=C0103,R0904
-class TestUri_GivenFileSchemeWithNoHeadSlashAndWithBackslash(unittest.TestCase): 
+class TestLocalFileUri_GivenWindowsStylePath(unittest.TestCase): 
     
     def setUp(self):
-        self.uri_unicode = u"file://c:/a é\a.pdf"
-        self.uri = Uri(self.uri_unicode)
-        
-        
+        self.uri_unicode = u"file:///c:/a é/a.pdf"
+        self.uri = LocalFileUri(self.uri_unicode)
+        
+        
+    @patch('os.path', _fake_os_path_windows())
     def should_have_file_as_scheme(self):
         self.assertEquals("file", self.uri.scheme)
     
     
+    @patch('os.path', _fake_os_path_windows())
     def should_have_no_netloc(self):
         self.assertEquals("", self.uri.netloc)
         
         
-    def should_all_is_in_the_path(self):
-        self.assertEquals(u"c:/a é\a.pdf", self.uri.path)
-        
-        
-    def should_not_nomalised_path(self):
-        self.assertEquals(u"c:/a é\a.pdf", self.uri.path)
-        
-    
+    @patch('os.path', _fake_os_path_windows())
+    def should_have_path_without_first_slash(self):
+        self.assertEquals(u"c:/a é/a.pdf", self.uri.path)
+        
+        
+    @patch('os.path', _fake_os_path_windows())
+    def should_have_same_original_path_as_path(self):
+        self.assertEquals(self.uri.path, self.uri.original_path)
+        
+        
+    @patch('os.path', _fake_os_path_windows())
     def should_have_no_query(self):
         self.assertEquals("", self.uri.query)
         
         
+    @patch('os.path', _fake_os_path_windows())
     def should_have_the_same_full_uri(self):
         self.assertEquals(self.uri_unicode, self.uri.uri)
         
         
+    @patch('os.path', _fake_os_path_windows())
     def test_unicode_property_should_have_the_full_uri(self):
         self.assertEquals(self.uri_unicode, unicode(self.uri))
         
@@ -266,27 +537,42 @@
         self.assertTrue(self.uri_base != self.uri_diff_all)
         
     
-class TestUriComparisons_GivenFileScheme(unittest.TestCase):
-    
-    def setUp(self):
-        self.uri_unix_base = Uri(u"file:///path/a é/fé.pdf")
-        self.uri_unix_diff_path = Uri(u"file:///path/a e/fé.pdf")
-        self.uri_win_base = Uri(u"file://c:\path\a é\fé.pdf")
-        self.uri_win_diff_path = Uri(u"file://c:\path\a é\fe.pdf")
-        self.uri_win_base_norm = Uri(u"file://c:/path/a é/fé.pdf")
-        
-        
-    def test_normalisation_should_be_ignored(self):
-        self.assertTrue(self.uri_win_base != self.uri_win_base_norm)
-        
-    
+class TestLocalFileUriComparisons_GivenUnixStylePaths(unittest.TestCase):
+    
+    def setUp(self):
+        self.uri_unix_base = LocalFileUri(u"file:///path/a é/fé.pdf")
+        self.uri_unix_diff_path = LocalFileUri(u"file:///path/a e/fé.pdf")
+        
+        
+    @patch('os.path', _fake_os_path_unix())
+    def test_same_instances_should_be_equal(self):
+        self.assertTrue(self.uri_unix_base == self.uri_unix_base)
+        
+        
+    @patch('os.path', _fake_os_path_unix())
+    def test_one_part_diff_should_not_be_equal(self):
+        self.assertTrue(self.uri_unix_base != self.uri_unix_diff_path)
+
+
+class TestLocalFileUriComparisons_GivenWindowsStylePaths(unittest.TestCase):
+    
+    def setUp(self):
+        self.uri_win_base = LocalFileUri(u"file:///c:/path/a é/fé.pdf")
+        self.uri_win_diff_path = LocalFileUri(u"file:///c:/path/a é/fe.pdf")
+        self.uri_win_short = LocalFileUri(u"file:///c:/docum~1")
+        self.uri_win_long = LocalFileUri(u"file:///c:/documents and settings")
+        
+        
+    @patch('os.path', _fake_os_path_windows())
     def test_same_instances_should_be_equal(self):
         self.assertTrue(self.uri_win_base == self.uri_win_base)
-        self.assertTrue(self.uri_unix_base == self.uri_unix_base)
-        
-        
+        
+        
+    @patch('os.path', _fake_os_path_windows())
     def test_one_part_diff_should_not_be_equal(self):
         self.assertTrue(self.uri_win_base != self.uri_win_diff_path)
-        self.assertTrue(self.uri_unix_base != self.uri_unix_diff_path)
-        
-        
\ No newline at end of file
+        
+        
+    @patch('os.path', _fake_os_path_windows())
+    def test_does_not_handle_short_names(self):
+        self.assertTrue(self.uri_win_short != self.uri_win_long)        

=== modified file 'slidepresenterview/utils/uri.py'
--- slidepresenterview/utils/uri.py	2010-05-17 04:15:53 +0000
+++ slidepresenterview/utils/uri.py	2010-06-02 05:43:25 +0000
@@ -4,6 +4,7 @@
 # https://launchpad.net/slidepresenterview
 #
 # Copyright (C) 2010 Felix-Antoine Bourbonnais <bouf10pub _AT@. rubico.info>
+# Copyright (C) 2010 Claude Bolduc <claude.bolduc _AT@. gmail.com>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -25,21 +26,67 @@
 import os
 from urllib2 import urlparse
 
-__all__ = ['Uri', 'file_path_to_uri']
-
-
-def file_path_to_uri(file_path):
-    """
-    Converts a file path into a full Uri.
+__all__ = ['create_uri_from_uri_string', 'create_uri_from_file_path',
+           'Uri', 'LocalFileUri']
+
+
+def create_uri_from_uri_string(uri_string):
+    """
+    Factory to create the right Uri type for a given uri_string.
+    """
+    uri_split = urlparse.urlsplit(uri_string, allow_fragments=False)
+        
+    if uri_split.scheme == 'file' and uri_split.netloc == '':
+        return LocalFileUri(uri_string)
+    
+    return Uri(uri_string)        
+
+
+def create_uri_from_file_path(file_path):
+    """
+    Converts a file path into a full LocalFileUri.
+    
+    Paths are expanded for environment variables.
     
     If the path is relative, it will be extended to the corresponding
     absolute location.
     
-    Paths are normalised.
+    Paths are normalized and case normalized.
+    
+    Windows-specific behavior
+    -------------------------
+    
+    Backslashes are converted to slashes.
+     
+    Short names (old DOS-style) are not expanded to their long name 
+    correspondence.
+    For example, 'C:\\PROGRA~1' is not expanded to 'C:\\Program Files'.
     """
+    file_path_norm = _normalize_file_path(file_path)
+    file_path_uri = _normalize_windows_specific_uri(file_path_norm)
+    return LocalFileUri("file://%s" % file_path_uri, file_path)
+
+
+def _normalize_file_path(file_path):
+    file_path = os.path.expanduser(file_path)
+    file_path = os.path.expandvars(file_path)
     file_path = os.path.abspath(file_path)
     file_path = os.path.normpath(file_path)
-    return Uri("file://%s" % file_path)
+    file_path = os.path.normcase(file_path)
+    return file_path
+
+
+def _normalize_windows_specific_uri(file_path):
+    file_path = file_path.replace('\\', '/')
+    if _is_windows_style_absolute_path(file_path):
+        file_path = "/" + file_path
+        
+    return file_path
+
+
+def _is_windows_style_absolute_path(file_path):
+    drive, _ = os.path.splitdrive(file_path)
+    return drive != ""
 
 
 class Uri(object):
@@ -82,59 +129,173 @@
         """
         @raise ValueError: Invalid (malformed) uri string.
         """
-        if len(uri_string) == 0:
-            raise ValueError("Invalid (empty) URI.")
-        
-        uri_split = urlparse.urlsplit(uri_string, allow_fragments=False)
-        
-        self.scheme = uri_split.scheme
-        if self.scheme == 'file':
-            self.netloc = ''
-            self.path = uri_split.netloc + uri_split.path
-        else:
-            self.netloc = uri_split.netloc
-            self.path = uri_split.path
-        self.query = uri_split.query
+        self._decompose_uri(uri_string)
         
         if len(self.scheme) == 0:
             raise ValueError("Invalid URI: scheme is mandatory.")
         if len(self.path) == 0:
             raise ValueError("Invalid URI: path is mandatory.")
-    
-    
+        
+        
+    def _decompose_uri(self, uri_string):
+        uri_split = urlparse.urlsplit(uri_string, allow_fragments=False)
+        
+        self.scheme = uri_split.scheme
+        self.netloc = uri_split.netloc
+        self.path = uri_split.path
+        self.query = uri_split.query
+
+
     @property
     def uri(self):
         uri = "%s://%s%s" % (self.scheme, self.netloc, self.path)
         if len(self.query) > 0:
             uri = "%s?%s" % (uri, self.query)
         return uri
-    
+
     
     def __str__(self):
         return self.uri
     
     
     def __cmp__(self, other_uri):
-        return self._cmp_textual_uris(self.uri, other_uri.uri)
-        
-        
-    def _cmp_textual_uris(self, uri_textual, other_uri_textual):
-        import unicodedata, sys
-        
-        try:
-            encoding = sys.stdout.encoding
-        except:                # Probably not executed by a human...
-            encoding = 'UTF-8' # So assume UTF-8 since our .py are in utf8.
-            
-        if not isinstance(uri_textual, unicode):
-            uri_unicode = unicode(uri_textual, encoding)
-        else:
-            uri_unicode = uri_textual
-        
-        if not isinstance(other_uri_textual, unicode):
-            other_uri_unicode = unicode(other_uri_textual, encoding)
-        else:
-            other_uri_unicode = other_uri_textual
-            
-        return cmp( unicodedata.normalize('NFC', uri_unicode),
-                    unicodedata.normalize('NFC', other_uri_unicode) )
\ No newline at end of file
+        return self._cmp_textual_strings(self.uri, other_uri.uri)
+        
+        
+    def _cmp_textual_strings(self, string, other_string):
+        import unicodedata
+        
+        string_unicode = self._ensure_string_is_in_unicode(string)
+        other_string_unicode = self._ensure_string_is_in_unicode(other_string)
+           
+        return cmp( unicodedata.normalize('NFC', string_unicode),
+                    unicodedata.normalize('NFC', other_string_unicode) )
+        
+        
+    def _ensure_string_is_in_unicode(self, string):
+        if isinstance(string, unicode):
+            return string
+        else:
+            encoding = self._get_encoding()
+            return unicode(string, encoding)
+        
+        
+    def _get_encoding(self):
+        import sys
+        
+        try:
+            return sys.stdout.encoding
+        except:            # Probably not executed by a human...
+            return 'UTF-8' # So assume UTF-8 since our .py are in utf8.
+        
+        
+class LocalFileUri(Uri):
+    """
+    Represents a specialization of an Uri for a local file.
+    
+    We are really picky with the syntax of Uris for the local files.
+    Each path of a Uri on a local file should be absolute, expansed and
+    case normalized. It is easy to create a LocalFileUri from any local
+    file path, just use the create_uri_from_file_path() function.
+    
+    Others differences with general Uris are
+    ---------------------------------------- 
+
+      - LocalFileUri objects have an empty network location.
+      - The comparison between LocalFileUri objects is based on verifying if
+        the paths lead to the same file (if possible).
+      - LocalFileUri also have a property original_path that allows to keep
+        track of the original path that created an Uri. This path can be
+        relative, unexpanded and not case normalized.
+        
+    Special considerations for Windows Uris
+    ---------------------------------------
+    
+    We consider that the ONLY valid Uris are of the form:
+    
+      file:///<drive_letter>:/path/to/the/file.<extension>
+    
+    Note that 
+      - three slashes must be used after 'file:';
+      - the normal colon (':') must be used after the drive letter (not '|');
+      - slashes must be used in the path (no backslashes).
+      
+    However, to ease manipulation of path, the path property for Windows local
+    file will always remove the slash before the drive letter.
+          
+    @ivar scheme: The protocol/scheme ('file' of course)
+    @ivar netloc: The network location (empty for local files)
+    @ivar path: The complete resource path (absolute, expanded and case 
+                normalized)
+    @ivar original_path: The path used to create this Uri.
+    @ivar query: The query 
+    @ivar uri: The full uri (unicode string)
+    """
+    
+    def __init__(self, uri_string, original_path=None):
+        """
+        @param original_path: The original path of the file.
+                              The property path will be used if original_path
+                              is None.
+        
+        @raise ValueError: Invalid (malformed) uri string.
+        """
+        Uri.__init__(self, uri_string)        
+
+        if len(self.netloc) != 0:
+            raise ValueError("Invalid local file URI: cannot have network "
+                             + "location.")
+
+        if original_path is None:
+            original_path = self.path
+        else:
+            if self._cmp_file_paths(self.path, original_path) != 0:
+                raise ValueError("Invalid local file URI: original path is "
+                                 + "invalid.")
+            
+        self.original_path = original_path
+        
+        
+    @property
+    def path(self):
+        if self._path != "":
+            path_without_first_slash = self._path[1:]
+            if _is_windows_style_absolute_path(path_without_first_slash):
+                return path_without_first_slash
+        
+        return self._path
+
+
+    @path.setter
+    def path(self, value):
+        self._path = value
+        
+        
+    @property
+    def uri(self):
+        """
+        Override the uri property to make sure to use the correct path.
+        """
+        uri = "%s://%s%s" % (self.scheme, self.netloc, self._path)
+        if len(self.query) > 0:
+            uri = "%s?%s" % (uri, self.query)
+        return uri
+    
+    
+    def __cmp__(self, other_uri):
+        return self._cmp_file_paths(self.path, other_uri.path)
+        
+        
+    def _cmp_file_paths(self, path, other_path):
+        try:
+            is_same_file = os.path.samefile(path, other_path)
+            if is_same_file:
+                return 0
+            else:
+                return cmp(path, other_path)
+        except AttributeError: # We do our best to make a comparison.
+            path = _normalize_file_path(path)
+            path = _normalize_windows_specific_uri(path)
+            other_path = _normalize_file_path(other_path)
+            other_path = _normalize_windows_specific_uri(other_path)
+            return self._cmp_textual_strings(path, other_path)


Follow ups