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```