Permalink
Browse files

Add support for usernames and passwords in URLs.

  • Loading branch information...
1 parent cc6a56b commit 8a97600e8728c0a92c33e75efdfcb5ae43c6805e Arthur Grunseid committed Dec 16, 2011
Showing with 337 additions and 198 deletions.
  1. +22 −9 API.md
  2. +1 −1 furl/__init__.py
  3. +43 −6 furl/furl.py
  4. +271 −182 tests/test_furl.py
View
31 API.md
@@ -3,13 +3,15 @@
***
### Basics
-furl objects let you access and modify the six major components of a URL
+furl objects let you access and modify the components of a URL
```
-scheme://host[:port]/path?query#fragment
+scheme://username:password@host:port/path?query#fragment
```
* __scheme__ is the scheme string, all lowercase.
+ * __username__ is the username string for authentication.
+ * __password__ is the password string for authentication with __username__.
* __host__ is the domain name, IPv4, or IPv6 address as a string. Domain names
are all lowercase.
* __port__ is an integer or None. A value of None means no port specified and
@@ -21,14 +23,15 @@ scheme://host[:port]/path?query#fragment
***
-### Scheme, Host, Port, and Network Location
+### Scheme, Username, Password, Host, Port, and Network Location
-__scheme__ and __host__ are strings and __port__ is an integer or None.
+__scheme__, __username__, __password__, and __host__ are strings. __port__ is an
+integer or None.
```python
->>> f = furl('http://www.google.com:99/')
->>> f.scheme, f.host, f.port
-('http', 'www.google.com', 99)
+>>> f = furl('http://user:pass@www.google.com:99/')
+>>> f.scheme, f.username, f.password, f.host, f.port
+('http', 'user', 'pass', 'www.google.com', 99)
```
furl infers the default port for common schemes
@@ -43,15 +46,19 @@ furl infers the default port for common schemes
None
```
-__netloc__ is the string combination of __host__ and __port__, not including
-__port__ if it is None or the default port for the provided __scheme__.
+__netloc__ is the string combination of __username__, __password__, __host__,
+and __port__, not including __port__ if it is None or the default port for the
+provided __scheme__.
```python
>>> furl('http://www.google.com/').netloc
'www.google.com'
>>> furl('http://www.google.com:99/').netloc
'www.google.com:99'
+
+>>> furl('http://user:pass@www.google.com:99/').netloc
+'user:pass@www.google.com:99'
```
***
@@ -273,6 +280,9 @@ __set()__ sets items of a furl object with the optional arguments
separator between the fragment path and the fragment query.
* __host__: Host string to adopt.
* __port__: Port number to adopt.
+ * __username__: Username string to adopt.
+ * __password__: password string to adopt.
+
```python
>>> furl().set(scheme='https', host='secure.google.com', port=99,
@@ -297,6 +307,9 @@ __remove()__ removes items from a furl object with the optional arguments
fragment's path string.
* __fragment_args__: A list of query keys to remove from the fragment's query,
if they exist.
+ * __username__: If True, remove the username, if it exists.
+ * __password__: If True, remove the password, if it exists.
+
```python
>>> url = 'https://secure.google.com:99/a/path/?some=args#great job'
View
@@ -10,7 +10,7 @@
# License: Build Amazing Things (Unlicense)
__title__ = 'furl'
-__version__ = '0.2.1'
+__version__ = '0.2.2'
__author__ = 'Arthur Grunseid'
__license__ = 'Unlicense'
__copyright__ = 'LOLOLOL'
View
@@ -347,6 +347,8 @@ class furl(PathCompositionInterface, QueryCompositionInterface,
Attributes:
DEFAULT_PORTS: Map of various URL schemes to their default ports. Scheme
strings are lowercase.
+ username: Username string for authentication.
+ password: Password string for authentication with <username>.
scheme: URL scheme ('http', 'https', etc). All lowercase.
host: URL host (domain, IPv4 address, or IPv6 address), not including
port. All lowercase.
@@ -358,6 +360,8 @@ class furl(PathCompositionInterface, QueryCompositionInterface,
fragment: Fragment object from FragmentCompositionInterface.
"""
DEFAULT_PORTS = {
+ 'ftp' : 21,
+ 'ssh' : 22,
'http' : 80,
'https' : 443,
}
@@ -370,6 +374,8 @@ def __init__(self, url=None):
QueryCompositionInterface.__init__(self)
FragmentCompositionInterface.__init__(self)
+ self.username = ''
+ self.password = ''
self.scheme = ''
self._host = ''
self._port = None
@@ -429,10 +435,17 @@ def port(self, port):
@property
def netloc(self):
+ userpass = self.username
+ if self.password:
+ userpass += ':' + self.password
+ if userpass:
+ userpass += '@'
+
netloc = self.host
if self.port and self.port != self.DEFAULT_PORTS.get(self.scheme):
- netloc = '%s:%i' % (self.host, self.port)
- return netloc
+ netloc += ':' + str(self.port)
+
+ return userpass + netloc
@netloc.setter
def netloc(self, netloc):
@@ -444,7 +457,16 @@ def netloc(self, netloc):
# Raises ValueError on malformed IPv6 addresses.
urlparse.urlsplit('http://%s/' % netloc)
- host = port = None
+ username = password = host = ''
+ port = None
+
+ if '@' in netloc:
+ userpass, netloc = netloc.split('@', 1)
+ if ':' in userpass:
+ username, password = userpass.split(':', 1)
+ else:
+ username = userpass
+
if ':' in netloc:
# IPv6 address literal.
if ']' in netloc:
@@ -465,6 +487,8 @@ def netloc(self, netloc):
# exception is raised when assigning self.port self.host isn't updated.
self.port = port # Raises ValueError on invalid port.
self.host = host
+ self.username = username
+ self.password = password
@property
def url(self):
@@ -517,9 +541,10 @@ def add(self, args=None, path=None, fragment_path=None, fragment_args=None,
self.fragment.add(path=fragment_path, args=fragment_args)
return self
- def set(self, args=None, path=None, fragment=None, scheme=None, netloc=None,
+ def set(self, args=None, path=None, fragment=None, scheme=None, netloc='',
fragment_path=None, fragment_args=None, fragment_separator=None,
- host=None, port=None, query=None, query_params=None):
+ host='', port=None, query=None, query_params=None,
+ username='', password=''):
"""
Set components of a url and return this furl instance, <self>.
@@ -561,6 +586,8 @@ def set(self, args=None, path=None, fragment=None, scheme=None, netloc=None,
between the fragment path and fragment query.
host: Host string to adopt.
port: Port number to adopt.
+ username: Username string to adopt.
+ password: Password string to adopt.
Raises:
ValueError on invalid port.
UserWarning if <netloc> and (<host> and/or <port>) are provided.
@@ -597,6 +624,10 @@ def set(self, args=None, path=None, fragment=None, scheme=None, netloc=None,
self.netloc, self.port = oldnetloc, oldport
raise
+ if username:
+ self.username = username
+ if password:
+ self.password = password
if scheme:
self.scheme = scheme
if host:
@@ -615,7 +646,7 @@ def set(self, args=None, path=None, fragment=None, scheme=None, netloc=None,
def remove(self, args=None, path=None, fragment=None, query=None,
query_params=None, port=None, fragment_path=None,
- fragment_args=None):
+ fragment_args=None, username=None, password=None):
"""
Remove components of url and return this furl instance, <self>.
@@ -635,8 +666,14 @@ def remove(self, args=None, path=None, fragment=None, query=None,
fragment's path string.
fragment_args: A list of query keys to remove from the fragment's query,
if they exist.
+ username: If True, remove the username, if it exists.
+ password: If True, remove the password, if it exists.
Returns: <self>.
"""
+ if username:
+ self.username = ''
+ if password:
+ self.password = ''
if port:
self.port = None
self.path.remove(path)
Oops, something went wrong.

0 comments on commit 8a97600

Please sign in to comment.