Skip to content
This repository

Allow %loadpy to load remote URLs that don't end in .py #1161

Merged
merged 5 commits into from over 2 years ago

2 participants

Fernando Perez Thomas Kluyver
Fernando Perez
Owner

Will also fix same encoding bug we found for notebooks next.

IPython/core/magic.py
@@ -2151,7 +2151,11 @@ def magic_loadpy(self, arg_s):
2151 2151
         %loadpy http://www.example.com/myscript.py
2152 2152
         """
2153 2153
         arg_s = unquote_filename(arg_s)
2154  
-        if not arg_s.endswith('.py'):
  2154
+        if not arg_s.startswith(('http://', 'https://')) \
  2155
+          and not arg_s.endswith('.py'):
  2156
+            # Local files must be .py; for remote URLs it's possible that the
  2157
+            # fetch URL doesn't have a .py in it (many servers have an opaque
  2158
+            # URL, such as scipy-central.org).
2155 2159
             raise ValueError('%%load only works with .py files: %s' % arg_s)
2156 2160
         if arg_s.startswith('http'):
1
Thomas Kluyver Collaborator

Don't forget to change this one as well (same issue with local files like 'httpx.py')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fernando Perez
Owner

Reviewed by @takluyver and @minrk on IRC in addition to what's here, merging now.

Fernando Perez fperez closed this December 15, 2011
Fernando Perez fperez merged commit 3deb45e into from December 15, 2011
Fernando Perez fperez referenced this pull request from a commit January 10, 2012
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 27 additions and 7 deletions. Show diff stats Hide diff stats

  1. 34  IPython/core/magic.py
34  IPython/core/magic.py
@@ -96,6 +96,9 @@ def needs_local_scope(func):
96 96
 # Used for exception handling in magic_edit
97 97
 class MacroToEdit(ValueError): pass
98 98
 
  99
+# Taken from PEP 263, this is the official encoding regexp.
  100
+_encoding_declaration_re = re.compile(r"^#.*coding[:=]\s*([-\w.]+)")
  101
+
99 102
 #***************************************************************************
100 103
 # Main class implementing Magic functionality
101 104
 
@@ -2151,16 +2154,33 @@ def magic_loadpy(self, arg_s):
2151 2154
         %loadpy http://www.example.com/myscript.py
2152 2155
         """
2153 2156
         arg_s = unquote_filename(arg_s)
2154  
-        if not arg_s.endswith('.py'):
  2157
+        remote_url = arg_s.startswith(('http://', 'https://'))
  2158
+        local_url = not remote_url
  2159
+        if local_url and not arg_s.endswith('.py'):
  2160
+            # Local files must be .py; for remote URLs it's possible that the
  2161
+            # fetch URL doesn't have a .py in it (many servers have an opaque
  2162
+            # URL, such as scipy-central.org).
2155 2163
             raise ValueError('%%load only works with .py files: %s' % arg_s)
2156  
-        if arg_s.startswith('http'):
  2164
+        if remote_url:
2157 2165
             import urllib2
2158  
-            response = urllib2.urlopen(arg_s)
2159  
-            content = response.read()
  2166
+            fileobj = urllib2.urlopen(arg_s)
  2167
+            # While responses have a .info().getencoding() way of asking for
  2168
+            # their encoding, in *many* cases the return value is bogus.  In
  2169
+            # the wild, servers serving utf-8 but declaring latin-1 are
  2170
+            # extremely common, as the old HTTP standards specify latin-1 as
  2171
+            # the default but many modern filesystems use utf-8.  So we can NOT
  2172
+            # rely on the headers.  Short of building complex encoding-guessing
  2173
+            # logic, going with utf-8 is a simple solution likely to be right
  2174
+            # in most real-world cases.
  2175
+            linesource = fileobj.read().decode('utf-8', 'replace').splitlines()
2160 2176
         else:
2161  
-            with open(arg_s) as f:
2162  
-                content = f.read()
2163  
-        self.set_next_input(content)
  2177
+            fileobj = linesource = open(arg_s)
  2178
+        
  2179
+        # Strip out encoding declarations
  2180
+        lines = [l for l in linesource if not _encoding_declaration_re.match(l)]
  2181
+        fileobj.close()
  2182
+        
  2183
+        self.set_next_input(os.linesep.join(lines))
2164 2184
 
2165 2185
     def _find_edit_target(self, args, opts, last_call):
2166 2186
         """Utility method used by magic_edit to find what to edit."""
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.