1<h1 align="center">
2  <img src="api.svg" width="496px" height="101px" alt="furl API">
3</h1>
4
5
6### Basics
7
8furl objects let you access and modify the various components of a URL.
9
10```
11scheme://username:password@host:port/path?query#fragment
12```
13
14 * __scheme__ is the scheme string (all lowercase) or None. None means no
15   scheme. An empty string means a protocol relative URL, like
16   `//www.google.com`.
17 * __username__ is the username string for authentication.
18 * __password__ is the password string for authentication with __username__.
19 * __host__ is the domain name, IPv4, or IPv6 address as a string. Domain names
20  are all lowercase.
21 * __port__ is an integer or None. A value of None means no port specified and
22  the default port for the given __scheme__ should be inferred, if possible
23  (e.g. port 80 for the scheme `http`).
24 * __path__ is a Path object comprised of path segments.
25 * __query__ is a Query object comprised of key:value query arguments.
26 * __fragment__ is a Fragment object comprised of a Path object and Query object
27   separated by an optional `?` separator.
28
29
30
31### Scheme, Username, Password, Host, Port, Network Location, and Origin
32
33__scheme__, __username__, __password__, and __host__ are strings or
34None. __port__ is an integer or None.
35
36```python
37>>> f = furl('http://user:pass@www.google.com:99/')
38>>> f.scheme, f.username, f.password, f.host, f.port
39('http', 'user', 'pass', 'www.google.com', 99)
40```
41
42furl infers the default port for common schemes.
43
44```python
45>>> f = furl('https://secure.google.com/')
46>>> f.port
47443
48
49>>> f = furl('unknown://www.google.com/')
50>>> print(f.port)
51None
52```
53
54__netloc__ is the string combination of __username__, __password__, __host__,
55and __port__, not including __port__ if it's None or the default port for the
56provided __scheme__.
57
58```python
59>>> furl('http://www.google.com/').netloc
60'www.google.com'
61
62>>> furl('http://www.google.com:99/').netloc
63'www.google.com:99'
64
65>>> furl('http://user:pass@www.google.com:99/').netloc
66'user:pass@www.google.com:99'
67```
68
69__origin__ is the string combination of __scheme__, __host__, and __port__, not
70including __port__ if it's None or the default port for the provided __scheme__.
71
72```python
73>>> furl('http://www.google.com/').origin
74'http://www.google.com'
75
76>>> furl('http://www.google.com:99/').origin
77'http://www.google.com:99'
78```
79
80
81
82### Path
83
84URL paths in furl are Path objects that have __segments__, a list of zero or
85more path segments that can be manipulated directly. Path segments in
86__segments__ are percent-decoded and all interaction with __segments__ should
87take place with percent-decoded strings.
88
89```python
90>>> f = furl('http://www.google.com/a/large%20ish/path')
91>>> f.path
92Path('/a/large ish/path')
93>>> f.path.segments
94['a', 'large ish', 'path']
95>>> str(f.path)
96'/a/large%20ish/path'
97```
98
99#### Manipulation
100
101```python
102>>> f.path.segments = ['a', 'new', 'path', '']
103>>> str(f.path)
104'/a/new/path/'
105
106>>> f.path = 'o/hi/there/with%20some%20encoding/'
107>>> f.path.segments
108['o', 'hi', 'there', 'with some encoding', '']
109>>> str(f.path)
110'/o/hi/there/with%20some%20encoding/'
111
112>>> f.url
113'http://www.google.com/o/hi/there/with%20some%20encoding/'
114
115>>> f.path.segments = ['segments', 'are', 'maintained', 'decoded', '^`<>[]"#/?']
116>>> str(f.path)
117'/segments/are/maintained/decoded/%5E%60%3C%3E%5B%5D%22%23%2F%3F'
118```
119
120A path that starts with `/` is considered absolute, and a Path can be absolute
121or not as specified (or set) by the boolean attribute __isabsolute__. URL Paths
122have a special restriction: they must be absolute if a __netloc__ (username,
123password, host, and/or port) is present. This restriction exists because a URL
124path must start with `/` to separate itself from the __netloc__, if
125present. Fragment Paths have no such limitation and __isabsolute__ and can be
126True or False without restriction.
127
128Here's a URL Path example that illustrates how __isabsolute__ becomes True and
129read-only in the presence of a __netloc__.
130
131```python
132>>> f = furl('/url/path')
133>>> f.path.isabsolute
134True
135>>> f.path.isabsolute = False
136>>> f.url
137'url/path'
138>>> f.host = 'blaps.ru'
139>>> f.url
140'blaps.ru/url/path'
141>>> f.path.isabsolute
142True
143>>> f.path.isabsolute = False
144Traceback (most recent call last):
145  ...
146AttributeError: Path.isabsolute is True and read-only for URLs with a netloc (a username, password, host, and/or port). URL paths must be absolute if a netloc exists.
147>>> f.url
148'blaps.ru/url/path'
149```
150
151Conversely, the __isabsolute__ attribute of Fragment Paths isn't bound by the
152same read-only restriction. URL fragments are always prefixed by a `#` character
153and don't need to be separated from the __netloc__.
154
155```python
156>>> f = furl('http://www.google.com/#/absolute/fragment/path/')
157>>> f.fragment.path.isabsolute
158True
159>>> f.fragment.path.isabsolute = False
160>>> f.url
161'http://www.google.com/#absolute/fragment/path/'
162>>> f.fragment.path.isabsolute = True
163>>> f.url
164'http://www.google.com/#/absolute/fragment/path/'
165```
166
167A path that ends with `/` is considered a directory, and otherwise considered a
168file. The Path attribute __isdir__ returns True if the path is a directory,
169False otherwise. Conversely, the attribute __isfile__ returns True if the path
170is a file, False otherwise.
171
172```python
173>>> f = furl('http://www.google.com/a/directory/')
174>>> f.path.isdir
175True
176>>> f.path.isfile
177False
178
179>>> f = furl('http://www.google.com/a/file')
180>>> f.path.isdir
181False
182>>> f.path.isfile
183True
184```
185
186A path can be normalized with __normalize()__, and __normalize()__ returns the
187Path object for method chaining.
188
189```python
190>>> f = furl('http://www.google.com////a/./b/lolsup/../c/')
191>>> f.path.normalize()
192>>> f.url
193'http://www.google.com/a/b/c/'
194```
195
196Path segments can also be appended with the slash operator, like with
197[pathlib.Path](https://docs.python.org/3/library/pathlib.html#operators).
198
199```python
200>>> from __future__ import division  # For Python 2.x.
201>>>
202>>> f = furl('path')
203>>> f.path /= 'with'
204>>> f.path = f.path / 'more' / 'path segments/'
205>>> f.url
206'/path/with/more/path%20segments/'
207```
208
209For a dictionary representation of a path, use __asdict()__.
210
211```python
212>>> f = furl('http://www.google.com/some/enc%20oding')
213>>> f.path.asdict()
214{ 'encoded': '/some/enc%20oding',
215  'isabsolute': True,
216  'isdir': False,
217  'isfile': True,
218  'segments': ['some', 'enc oding'] }
219```
220
221
222
223### Query
224
225URL queries in furl are Query objects that have __params__, a one dimensional
226[ordered multivalue dictionary](https://github.com/gruns/orderedmultidict) of
227query keys and values. Query keys and values in __params__ are percent-decoded
228and all interaction with __params__ should take place with percent-decoded
229strings.
230
231```python
232>>> f = furl('http://www.google.com/?one=1&two=2')
233>>> f.query
234Query('one=1&two=2')
235>>> f.query.params
236omdict1D([('one', '1'), ('two', '2')])
237>>> str(f.query)
238'one=1&two=2'
239```
240
241furl objects and Fragment objects (covered below) contain a Query object, and
242__args__ is provided as a shortcut on these objects to access __query.params__.
243
244```python
245>>> f = furl('http://www.google.com/?one=1&two=2')
246>>> f.query.params
247omdict1D([('one', '1'), ('two', '2')])
248>>> f.args
249omdict1D([('one', '1'), ('two', '2')])
250>>> f.args is f.query.params
251True
252```
253
254#### Manipulation
255
256__params__ is a one dimensional
257[ordered multivalue dictionary](https://github.com/gruns/orderedmultidict) that
258maintains method parity with Python's standard dictionary.
259
260```python
261>>> f.query = 'silicon=14&iron=26&inexorable%20progress=vae%20victus'
262>>> f.query.params
263omdict1D([('silicon', '14'), ('iron', '26'), ('inexorable progress', 'vae victus')])
264>>> del f.args['inexorable progress']
265>>> f.args['magnesium'] = '12'
266>>> f.args
267omdict1D([('silicon', '14'), ('iron', '26'), ('magnesium', '12')])
268```
269
270__params__ can also store multiple values for the same key because it's a
271multivalue dictionary.
272
273```python
274>>> f = furl('http://www.google.com/?space=jams&space=slams')
275>>> f.args['space']
276'jams'
277>>> f.args.getlist('space')
278['jams', 'slams']
279>>> f.args.addlist('repeated', ['1', '2', '3'])
280>>> str(f.query)
281'space=jams&space=slams&repeated=1&repeated=2&repeated=3'
282>>> f.args.popvalue('space')
283'slams'
284>>> f.args.popvalue('repeated', '2')
285'2'
286>>> str(f.query)
287'space=jams&repeated=1&repeated=3'
288```
289
290__params__ is one dimensional. If a list of values is provided as a query value,
291that list is interpretted as multiple values.
292
293```python
294>>> f = furl()
295>>> f.args['repeated'] = ['1', '2', '3']
296>>> f.add(args={'space':['jams', 'slams']})
297>>> str(f.query)
298'repeated=1&repeated=2&repeated=3&space=jams&space=slams'
299```
300
301This makes sense: URL queries are inherently one dimensional -- query values
302can't have native subvalues.
303
304See the [orderedmultimdict](https://github.com/gruns/orderedmultidict)
305documentation for more information on interacting with the ordered multivalue
306dictionary __params__.
307
308#### Parameters
309
310To produce an empty query argument, like `http://sprop.su/?param=`, set the
311argument's value to the empty string.
312
313```python
314>>> f = furl('http://sprop.su')
315>>> f.args['param'] = ''
316>>> f.url
317'http://sprop.su/?param='
318```
319
320To produce an empty query argument without a trailing `=`, use `None` as the
321parameter value.
322
323```python
324>>> f = furl('http://sprop.su')
325>>> f.args['param'] = None
326>>> f.url
327'http://sprop.su/?param'
328```
329
330__encode(delimiter='&', quote_plus=True, dont_quote='')__ can be used to encode
331query strings with delimiters like `;`, encode spaces as `+` instead of `%20`
332(i.e. application/x-www-form-urlencoded encoded), or avoid percent-encoding
333valid query charactes entirely (valid query characters are
334`/?:@-._~!$&'()*+,;=`).
335
336```python
337>>> f.query = 'space=jams&woofs=squeeze+dog'
338>>> f.query.encode()
339'space=jams&woofs=squeeze+dog'
340>>> f.query.encode(';')
341'space=jams;woofs=squeeze+dog'
342>>> f.query.encode(quote_plus=False)
343'space=jams&woofs=squeeze%20dog'
344```
345
346`dont_quote` accepts `True`, `False`, or a string of valid query characters to
347not percent-enode. If `True`, all valid query characters `/?:@-._~!$&'()*+,;=`
348aren't percent-encoded.
349
350```python
351>>> f.query = 'one,two/three'
352>>> f.query.encode()
353'one%2Ctwo%2Fthree'
354>>> f.query.encode(dont_quote=True)
355'one,two/three'
356>>> f.query.encode(dont_quote=',')
357'one,two%2Fthree'
358```
359
360For a dictionary representation of a query, use __asdict()__.
361
362```python
363>>> f = furl('http://www.google.com/?space=ja+ms&space=slams')
364>>> f.query.asdict()
365{ 'encoded': 'space=ja+ms&space=slams',
366  'params': [('space', 'ja ms'),
367             ('space', 'slams')] }
368```
369
370
371
372### Fragment
373
374URL fragments in furl are Fragment objects that have a Path __path__ and Query
375__query__ separated by an optional `?` __separator__.
376
377```python
378>>> f = furl('http://www.google.com/#/fragment/path?with=params')
379>>> f.fragment
380Fragment('/fragment/path?with=params')
381>>> f.fragment.path
382Path('/fragment/path')
383>>> f.fragment.query
384Query('with=params')
385>>> f.fragment.separator
386True
387```
388
389Manipulation of Fragments is done via the Fragment's Path and Query instances,
390__path__ and __query__.
391
392```python
393>>> f = furl('http://www.google.com/#/fragment/path?with=params')
394>>> str(f.fragment)
395'/fragment/path?with=params'
396>>> f.fragment.path.segments.append('file.ext')
397>>> str(f.fragment)
398'/fragment/path/file.ext?with=params'
399
400>>> f = furl('http://www.google.com/#/fragment/path?with=params')
401>>> str(f.fragment)
402'/fragment/path?with=params'
403>>> f.fragment.args['new'] = 'yep'
404>>> str(f.fragment)
405'/fragment/path?new=yep&with=params'
406```
407
408Creating hash-bang fragments with furl illustrates the use of Fragment's boolean
409attribute __separator__. When __separator__ is False, the `?` that separates
410__path__ and __query__ isn't included.
411
412```python
413>>> f = furl('http://www.google.com/')
414>>> f.fragment.path = '!'
415>>> f.fragment.args = {'a':'dict', 'of':'args'}
416>>> f.fragment.separator
417True
418>>> str(f.fragment)
419'!?a=dict&of=args'
420
421>>> f.fragment.separator = False
422>>> str(f.fragment)
423'!a=dict&of=args'
424>>> f.url
425'http://www.google.com/#!a=dict&of=args'
426```
427
428For a dictionary representation of a fragment, use __asdict()__.
429
430```python
431>>> f = furl('http://www.google.com/#path?args=args')
432>>> f.fragment.asdict()
433{ 'encoded': 'path?args=args',
434  'separator': True,
435  'path': { 'encoded': 'path',
436            'isabsolute': False,
437            'isdir': False,
438            'isfile': True,
439            'segments': ['path']},
440  'query': { 'encoded': 'args=args',
441             'params': [('args', 'args')]} }
442```
443
444
445
446### Encoding
447
448Furl handles encoding for you, and furl's philosophy on encoding is simple: raw
449URL strings should always be percent-encoded.
450
451```python
452>>> f = furl()
453>>> f.netloc = '%40user:%3Apass@google.com'
454>>> f.username, f.password
455'@user', ':pass'
456
457>>> f = furl()
458>>> f.path = 'supply%20percent%20encoded/path%20strings'
459>>> f.path.segments
460['supply percent encoded', 'path strings']
461
462>>> f.set(query='supply+percent+encoded=query+strings,+too')
463>>> f.query.params
464omdict1D([('supply percent encoded', 'query strings, too')])
465
466>>> f.set(fragment='percent%20encoded%20path?and+percent+encoded=query+too')
467>>> f.fragment.path.segments
468['percent encoded path']
469>>> f.fragment.args
470omdict1D([('and percent encoded', 'query too')])
471```
472
473Raw, non-URL strings should never be percent-encoded.
474
475```python
476>>> f = furl('http://google.com')
477>>> f.set(username='@prap', password=':porps')
478>>> f.url
479'http://%40prap:%3Aporps@google.com'
480
481>>> f = furl()
482>>> f.set(path=['path segments are', 'decoded', '<>[]"#'])
483>>> str(f.path)
484'/path%20segments%20are/decoded/%3C%3E%5B%5D%22%23'
485
486>>> f.set(args={'query parameters':'and values', 'are':'decoded, too'})
487>>> str(f.query)
488'query+parameters=and+values&are=decoded,+too'
489
490>>> f.fragment.path.segments = ['decoded', 'path segments']
491>>> f.fragment.args = {'and decoded':'query parameters and values'}
492>>> str(f.fragment)
493'decoded/path%20segments?and+decoded=query+parameters+and+values'
494```
495
496Python's
497[urllib.quote()](http://docs.python.org/library/urllib.html#urllib.quote) and
498[urllib.unquote()](http://docs.python.org/library/urllib.html#urllib.unquote)
499can be used to percent-encode and percent-decode path strings. Similarly,
500[urllib.quote_plus()](http://docs.python.org/library/urllib.html#urllib.quote_plus)
501and
502[urllib.unquote_plus()](http://docs.python.org/library/urllib.html#urllib.unquote_plus)
503can be used to percent-encode and percent-decode query strings.
504
505
506
507### Inline manipulation
508
509For quick, single-line URL manipulation, the __add()__, __set()__, and
510__remove()__ methods of furl objects manipulate various URL components and
511return the furl object for method chaining.
512
513```python
514>>> url = 'http://www.google.com/#fragment'
515>>> furl(url).add(args={'example':'arg'}).set(port=99).remove(fragment=True).url
516'http://www.google.com:99/?example=arg'
517```
518
519__add()__ adds items to a furl object with the optional arguments
520
521 * __args__: Shortcut for __query_params__.
522 * __path__: A list of path segments to add to the existing path segments, or a
523   path string to join with the existing path string.
524 * __query_params__: A dictionary of query keys and values to add to the query.
525 * __fragment_path__: A list of path segments to add to the existing fragment
526   path segments, or a path string to join with the existing fragment path
527   string.
528 * __fragment_args__: A dictionary of query keys and values to add to the
529   fragment's query.
530
531```python
532>>> f = furl('http://www.google.com/').add(
533...   path='/search', fragment_path='frag/path', fragment_args={'frag':'arg'})
534>>> f.url
535'http://www.google.com/search#frag/path?frag=args'
536```
537
538__set()__ sets items of a furl object with the optional arguments
539
540 * __args__: Shortcut for __query_params__.
541 * __path__: List of path segments or a path string to adopt.
542 * __scheme__: Scheme string to adopt.
543 * __netloc__: Network location string to adopt.
544 * __origin__: Origin string to adopt.
545 * __query__: Query string to adopt.
546 * __query_params__: A dictionary of query keys and values to adopt.
547 * __fragment__: Fragment string to adopt.
548 * __fragment_path__: A list of path segments to adopt for the fragment's path
549   or a path string to adopt as the fragment's path.
550 * __fragment_args__: A dictionary of query keys and values for the fragment's
551   query to adopt.
552 * __fragment_separator__: Boolean whether or not there should be a `?`
553   separator between the fragment path and the fragment query.
554 * __host__: Host string to adopt.
555 * __port__: Port number to adopt.
556 * __username__: Username string to adopt.
557 * __password__: password string to adopt.
558
559
560```python
561>>> f = furl().set(
562...   scheme='https', host='secure.google.com', port=99, path='index.html',
563...   args={'some':'args'}, fragment='great job')
564>>> f.url
565'https://secure.google.com:99/index.html?some=args#great%20job'
566```
567
568__remove()__ removes items from a furl object with the optional arguments
569
570 * __args__: Shortcut for __query_params__.
571 * __path__: A list of path segments to remove from the end of the existing path
572       segments list, or a path string to remove from the end of the existing
573       path string, or True to remove the entire path portion of the URL.
574 * __query__: A list of query keys to remove from the query, if they exist, or
575       True to remove the entire query portion of the URL.
576 * __query_params__: A list of query keys to remove from the query, if they
577       exist.
578 * __fragment__: If True, remove the entire fragment portion of the URL.
579 * __fragment_path__: A list of path segments to remove from the end of the
580       fragment's path segments, or a path string to remove from the end of the
581       fragment's path string, or True to remove the entire fragment path.
582 * __fragment_args__: A list of query keys to remove from the fragment's query,
583       if they exist.
584 * __username__: If True, remove the username, if it exists.
585 * __password__: If True, remove the password, if it exists.
586
587
588```python
589>>> url = 'https://secure.google.com:99/a/path/?some=args#great job'
590>>> furl(url).remove(args=['some'], path='path/', fragment=True, port=True).url
591'https://secure.google.com/a/'
592```
593
594
595
596### Miscellaneous
597
598Like [pathlib.Path](https://docs.python.org/3/library/pathlib.html#operators),
599path segments can be appended to a furl object's Path with the slash operator.
600
601```python
602>>> from __future__ import division  # For Python 2.x.
603>>> f = furl('http://www.google.com/path?example=arg#frag')
604>>> f /= 'add'
605>>> f = f / 'seg ments/'
606>>> f.url
607'http://www.google.com/path/add/seg%20ments/?example=arg#frag'
608```
609
610__tostr(query_delimiter='&', query_quote_plus=True, query_dont_quote='')__
611creates and returns a URL string. `query_delimiter`, `query_quote_plus`, and
612`query_dont_quote` are passed unmodified to `Query.encode()` as `delimiter`,
613`quote_plus`, and `dont_quote` respectively.
614
615```python
616>>> f = furl('http://spep.ru/?a+b=c+d&two%20tap=cat%20nap%24')
617>>> f.tostr()
618'http://spep.ru/?a+b=c+d&two+tap=cat+nap$'
619>>> f.tostr(query_delimiter=';', query_quote_plus=False)
620'http://spep.ru/?a%20b=c%20d;two%20tap=cat%20nap$'
621>>> f.tostr(query_dont_quote='$')
622'http://spep.ru/?a+b=c+d&two+tap=cat+nap$'
623```
624
625`furl.url` is a shortcut for `furl.tostr()`.
626
627```python
628>>> f.url
629'http://spep.ru/?a+b=c+d&two+tap=cat+nap$'
630>>> f.url == f.tostr() == str(f)
631True
632```
633
634__copy()__ creates and returns a new furl object with an identical URL.
635
636```python
637>>> f = furl('http://www.google.com')
638>>> f.copy().set(path='/new/path').url
639'http://www.google.com/new/path'
640>>> f.url
641'http://www.google.com'
642```
643
644__join()__ joins the furl object's URL with the provided relative or absolute
645URL and returns the furl object for method chaining. __join()__'s action is the
646same as navigating to the provided URL from the current URL in a web browser.
647
648```python
649>>> f = furl('http://www.google.com')
650>>> f.join('new/path').url
651'http://www.google.com/new/path'
652>>> f.join('replaced').url
653'http://www.google.com/new/replaced'
654>>> f.join('../parent').url
655'http://www.google.com/parent'
656>>> f.join('path?query=yes#fragment').url
657'http://www.google.com/path?query=yes#fragment'
658>>> f.join('unknown://www.yahoo.com/new/url/').url
659'unknown://www.yahoo.com/new/url/'
660```
661
662For a dictionary representation of a URL, use __asdict()__.
663
664```python
665>>> f = furl('https://xn--eckwd4c7c.xn--zckzah/path?args=args#frag')
666>>> f.asdict()
667{ 'url': 'https://xn--eckwd4c7c.xn--zckzah/path?args=args#frag',
668  'scheme': 'https',
669  'username': None
670  'password': None,
671  'host': 'ドメイン.テスト',
672  'host_encoded': 'xn--eckwd4c7c.xn--zckzah',
673  'port': 443,
674  'netloc': 'xn--eckwd4c7c.xn--zckzah',
675  'origin': 'https://xn--eckwd4c7c.xn--zckzah',
676  'path': { 'encoded': '/path',
677            'isabsolute': True,
678            'isdir': False,
679            'isfile': True,
680            'segments': ['path']},
681  'query': { 'encoded': 'args=args',
682             'params': [('args', 'args')]},
683  'fragment': { 'encoded': 'frag',
684                'path': { 'encoded': 'frag',
685                          'isabsolute': False,
686                          'isdir': False,
687                          'isfile': True,
688                          'segments': ['frag']},
689                'query': { 'encoded': '',
690                           'params': []},
691                'separator': True} }
692```