'
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/py.typed b/IKEA_scraper/.venv/Lib/site-packages/anyio/py.typed
deleted file mode 100644
index e69de29b..00000000
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/pytest_plugin.py b/IKEA_scraper/.venv/Lib/site-packages/anyio/pytest_plugin.py
deleted file mode 100644
index d0cc2fb5..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/anyio/pytest_plugin.py
+++ /dev/null
@@ -1,152 +0,0 @@
-from contextlib import contextmanager
-from inspect import isasyncgenfunction, iscoroutinefunction
-from typing import TYPE_CHECKING, Any, Dict, Iterator, Optional, Tuple, cast
-
-import pytest
-import sniffio
-
-from ._core._eventloop import get_all_backends, get_asynclib
-from .abc import TestRunner
-
-if TYPE_CHECKING:
- from _pytest.config import Config
-
-_current_runner: Optional[TestRunner] = None
-
-
-def extract_backend_and_options(backend: object) -> Tuple[str, Dict[str, Any]]:
- if isinstance(backend, str):
- return backend, {}
- elif isinstance(backend, tuple) and len(backend) == 2:
- if isinstance(backend[0], str) and isinstance(backend[1], dict):
- return cast(Tuple[str, Dict[str, Any]], backend)
-
- raise TypeError('anyio_backend must be either a string or tuple of (string, dict)')
-
-
-@contextmanager
-def get_runner(backend_name: str, backend_options: Dict[str, Any]) -> Iterator[TestRunner]:
- global _current_runner
- if _current_runner:
- yield _current_runner
- return
-
- asynclib = get_asynclib(backend_name)
- token = None
- if sniffio.current_async_library_cvar.get(None) is None:
- # Since we're in control of the event loop, we can cache the name of the async library
- token = sniffio.current_async_library_cvar.set(backend_name)
-
- try:
- backend_options = backend_options or {}
- with asynclib.TestRunner(**backend_options) as runner:
- _current_runner = runner
- yield runner
- finally:
- _current_runner = None
- if token:
- sniffio.current_async_library_cvar.reset(token)
-
-
-def pytest_configure(config: "Config") -> None:
- config.addinivalue_line('markers', 'anyio: mark the (coroutine function) test to be run '
- 'asynchronously via anyio.')
-
-
-def pytest_fixture_setup(fixturedef: Any, request: Any) -> None:
- def wrapper(*args, anyio_backend, **kwargs): # type: ignore[no-untyped-def]
- backend_name, backend_options = extract_backend_and_options(anyio_backend)
- if has_backend_arg:
- kwargs['anyio_backend'] = anyio_backend
-
- with get_runner(backend_name, backend_options) as runner:
- if isasyncgenfunction(func):
- gen = func(*args, **kwargs)
- try:
- value = runner.call(gen.asend, None)
- except StopAsyncIteration:
- raise RuntimeError('Async generator did not yield')
-
- yield value
-
- try:
- runner.call(gen.asend, None)
- except StopAsyncIteration:
- pass
- else:
- runner.call(gen.aclose)
- raise RuntimeError('Async generator fixture did not stop')
- else:
- yield runner.call(func, *args, **kwargs)
-
- # Only apply this to coroutine functions and async generator functions in requests that involve
- # the anyio_backend fixture
- func = fixturedef.func
- if isasyncgenfunction(func) or iscoroutinefunction(func):
- if 'anyio_backend' in request.fixturenames:
- has_backend_arg = 'anyio_backend' in fixturedef.argnames
- fixturedef.func = wrapper
- if not has_backend_arg:
- fixturedef.argnames += ('anyio_backend',)
-
-
-@pytest.hookimpl(tryfirst=True)
-def pytest_pycollect_makeitem(collector: Any, name: Any, obj: Any) -> None:
- if collector.istestfunction(obj, name):
- inner_func = obj.hypothesis.inner_test if hasattr(obj, 'hypothesis') else obj
- if iscoroutinefunction(inner_func):
- marker = collector.get_closest_marker('anyio')
- own_markers = getattr(obj, 'pytestmark', ())
- if marker or any(marker.name == 'anyio' for marker in own_markers):
- pytest.mark.usefixtures('anyio_backend')(obj)
-
-
-@pytest.hookimpl(tryfirst=True)
-def pytest_pyfunc_call(pyfuncitem: Any) -> Optional[bool]:
- def run_with_hypothesis(**kwargs: Any) -> None:
- with get_runner(backend_name, backend_options) as runner:
- runner.call(original_func, **kwargs)
-
- backend = pyfuncitem.funcargs.get('anyio_backend')
- if backend:
- backend_name, backend_options = extract_backend_and_options(backend)
-
- if hasattr(pyfuncitem.obj, 'hypothesis'):
- # Wrap the inner test function unless it's already wrapped
- original_func = pyfuncitem.obj.hypothesis.inner_test
- if original_func.__qualname__ != run_with_hypothesis.__qualname__:
- if iscoroutinefunction(original_func):
- pyfuncitem.obj.hypothesis.inner_test = run_with_hypothesis
-
- return None
-
- if iscoroutinefunction(pyfuncitem.obj):
- funcargs = pyfuncitem.funcargs
- testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
- with get_runner(backend_name, backend_options) as runner:
- runner.call(pyfuncitem.obj, **testargs)
-
- return True
-
- return None
-
-
-@pytest.fixture(params=get_all_backends())
-def anyio_backend(request: Any) -> Any:
- return request.param
-
-
-@pytest.fixture
-def anyio_backend_name(anyio_backend: Any) -> str:
- if isinstance(anyio_backend, str):
- return anyio_backend
- else:
- return anyio_backend[0]
-
-
-@pytest.fixture
-def anyio_backend_options(anyio_backend: Any) -> Dict[str, Any]:
- if isinstance(anyio_backend, str):
- return {}
- else:
- return anyio_backend[1]
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__init__.py b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/__init__.cpython-39.pyc
deleted file mode 100644
index 5c728b4e..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/__init__.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/buffered.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/buffered.cpython-39.pyc
deleted file mode 100644
index c4ecd760..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/buffered.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/file.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/file.cpython-39.pyc
deleted file mode 100644
index 53aae954..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/file.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/memory.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/memory.cpython-39.pyc
deleted file mode 100644
index d369cfbf..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/memory.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/stapled.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/stapled.cpython-39.pyc
deleted file mode 100644
index e75c3548..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/stapled.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/text.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/text.cpython-39.pyc
deleted file mode 100644
index ae00fc5c..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/text.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/tls.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/tls.cpython-39.pyc
deleted file mode 100644
index 0b183767..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/__pycache__/tls.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/buffered.py b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/buffered.py
deleted file mode 100644
index ee220caa..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/buffered.py
+++ /dev/null
@@ -1,116 +0,0 @@
-from dataclasses import dataclass, field
-from typing import Any, Callable, Mapping
-
-from .. import ClosedResourceError, DelimiterNotFound, EndOfStream, IncompleteRead
-from ..abc import AnyByteReceiveStream, ByteReceiveStream
-
-
-@dataclass(eq=False)
-class BufferedByteReceiveStream(ByteReceiveStream):
- """
- Wraps any bytes-based receive stream and uses a buffer to provide sophisticated receiving
- capabilities in the form of a byte stream.
- """
-
- receive_stream: AnyByteReceiveStream
- _buffer: bytearray = field(init=False, default_factory=bytearray)
- _closed: bool = field(init=False, default=False)
-
- async def aclose(self) -> None:
- await self.receive_stream.aclose()
- self._closed = True
-
- @property
- def buffer(self) -> bytes:
- """The bytes currently in the buffer."""
- return bytes(self._buffer)
-
- @property
- def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
- return self.receive_stream.extra_attributes
-
- async def receive(self, max_bytes: int = 65536) -> bytes:
- if self._closed:
- raise ClosedResourceError
-
- if self._buffer:
- chunk = bytes(self._buffer[:max_bytes])
- del self._buffer[:max_bytes]
- return chunk
- elif isinstance(self.receive_stream, ByteReceiveStream):
- return await self.receive_stream.receive(max_bytes)
- else:
- # With a bytes-oriented object stream, we need to handle any surplus bytes we get from
- # the receive() call
- chunk = await self.receive_stream.receive()
- if len(chunk) > max_bytes:
- # Save the surplus bytes in the buffer
- self._buffer.extend(chunk[max_bytes:])
- return chunk[:max_bytes]
- else:
- return chunk
-
- async def receive_exactly(self, nbytes: int) -> bytes:
- """
- Read exactly the given amount of bytes from the stream.
-
- :param nbytes: the number of bytes to read
- :return: the bytes read
- :raises ~anyio.IncompleteRead: if the stream was closed before the requested
- amount of bytes could be read from the stream
-
- """
- while True:
- remaining = nbytes - len(self._buffer)
- if remaining <= 0:
- retval = self._buffer[:nbytes]
- del self._buffer[:nbytes]
- return bytes(retval)
-
- try:
- if isinstance(self.receive_stream, ByteReceiveStream):
- chunk = await self.receive_stream.receive(remaining)
- else:
- chunk = await self.receive_stream.receive()
- except EndOfStream as exc:
- raise IncompleteRead from exc
-
- self._buffer.extend(chunk)
-
- async def receive_until(self, delimiter: bytes, max_bytes: int) -> bytes:
- """
- Read from the stream until the delimiter is found or max_bytes have been read.
-
- :param delimiter: the marker to look for in the stream
- :param max_bytes: maximum number of bytes that will be read before raising
- :exc:`~anyio.DelimiterNotFound`
- :return: the bytes read (not including the delimiter)
- :raises ~anyio.IncompleteRead: if the stream was closed before the delimiter
- was found
- :raises ~anyio.DelimiterNotFound: if the delimiter is not found within the
- bytes read up to the maximum allowed
-
- """
- delimiter_size = len(delimiter)
- offset = 0
- while True:
- # Check if the delimiter can be found in the current buffer
- index = self._buffer.find(delimiter, offset)
- if index >= 0:
- found = self._buffer[:index]
- del self._buffer[:index + len(delimiter):]
- return bytes(found)
-
- # Check if the buffer is already at or over the limit
- if len(self._buffer) >= max_bytes:
- raise DelimiterNotFound(max_bytes)
-
- # Read more data into the buffer from the socket
- try:
- data = await self.receive_stream.receive()
- except EndOfStream as exc:
- raise IncompleteRead from exc
-
- # Move the offset forward and add the new data to the buffer
- offset = max(len(self._buffer) - delimiter_size + 1, 0)
- self._buffer.extend(data)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/file.py b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/file.py
deleted file mode 100644
index deb5e623..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/file.py
+++ /dev/null
@@ -1,139 +0,0 @@
-from io import SEEK_SET, UnsupportedOperation
-from os import PathLike
-from pathlib import Path
-from typing import Any, BinaryIO, Callable, Dict, Mapping, Union, cast
-
-from .. import (
- BrokenResourceError, ClosedResourceError, EndOfStream, TypedAttributeSet, to_thread,
- typed_attribute)
-from ..abc import ByteReceiveStream, ByteSendStream
-
-
-class FileStreamAttribute(TypedAttributeSet):
- #: the open file descriptor
- file: BinaryIO = typed_attribute()
- #: the path of the file on the file system, if available (file must be a real file)
- path: Path = typed_attribute()
- #: the file number, if available (file must be a real file or a TTY)
- fileno: int = typed_attribute()
-
-
-class _BaseFileStream:
- def __init__(self, file: BinaryIO):
- self._file = file
-
- async def aclose(self) -> None:
- await to_thread.run_sync(self._file.close)
-
- @property
- def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
- attributes: Dict[Any, Callable[[], Any]] = {
- FileStreamAttribute.file: lambda: self._file,
- }
-
- if hasattr(self._file, 'name'):
- attributes[FileStreamAttribute.path] = lambda: Path(self._file.name)
-
- try:
- self._file.fileno()
- except UnsupportedOperation:
- pass
- else:
- attributes[FileStreamAttribute.fileno] = lambda: self._file.fileno()
-
- return attributes
-
-
-class FileReadStream(_BaseFileStream, ByteReceiveStream):
- """
- A byte stream that reads from a file in the file system.
-
- :param file: a file that has been opened for reading in binary mode
-
- .. versionadded:: 3.0
- """
-
- @classmethod
- async def from_path(cls, path: Union[str, PathLike]) -> 'FileReadStream':
- """
- Create a file read stream by opening the given file.
-
- :param path: path of the file to read from
-
- """
- file = await to_thread.run_sync(Path(path).open, 'rb')
- return cls(cast(BinaryIO, file))
-
- async def receive(self, max_bytes: int = 65536) -> bytes:
- try:
- data = await to_thread.run_sync(self._file.read, max_bytes)
- except ValueError:
- raise ClosedResourceError from None
- except OSError as exc:
- raise BrokenResourceError from exc
-
- if data:
- return data
- else:
- raise EndOfStream
-
- async def seek(self, position: int, whence: int = SEEK_SET) -> int:
- """
- Seek the file to the given position.
-
- .. seealso:: :meth:`io.IOBase.seek`
-
- .. note:: Not all file descriptors are seekable.
-
- :param position: position to seek the file to
- :param whence: controls how ``position`` is interpreted
- :return: the new absolute position
- :raises OSError: if the file is not seekable
-
- """
- return await to_thread.run_sync(self._file.seek, position, whence)
-
- async def tell(self) -> int:
- """
- Return the current stream position.
-
- .. note:: Not all file descriptors are seekable.
-
- :return: the current absolute position
- :raises OSError: if the file is not seekable
-
- """
- return await to_thread.run_sync(self._file.tell)
-
-
-class FileWriteStream(_BaseFileStream, ByteSendStream):
- """
- A byte stream that writes to a file in the file system.
-
- :param file: a file that has been opened for writing in binary mode
-
- .. versionadded:: 3.0
- """
-
- @classmethod
- async def from_path(cls, path: Union[str, PathLike],
- append: bool = False) -> 'FileWriteStream':
- """
- Create a file write stream by opening the given file for writing.
-
- :param path: path of the file to write to
- :param append: if ``True``, open the file for appending; if ``False``, any existing file
- at the given path will be truncated
-
- """
- mode = 'ab' if append else 'wb'
- file = await to_thread.run_sync(Path(path).open, mode)
- return cls(cast(BinaryIO, file))
-
- async def send(self, item: bytes) -> None:
- try:
- await to_thread.run_sync(self._file.write, item)
- except ValueError:
- raise ClosedResourceError from None
- except OSError as exc:
- raise BrokenResourceError from exc
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/memory.py b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/memory.py
deleted file mode 100644
index 91924f39..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/memory.py
+++ /dev/null
@@ -1,256 +0,0 @@
-from collections import OrderedDict, deque
-from dataclasses import dataclass, field
-from types import TracebackType
-from typing import Deque, Generic, List, NamedTuple, Optional, Type, TypeVar
-
-from .. import (
- BrokenResourceError, ClosedResourceError, EndOfStream, WouldBlock, get_cancelled_exc_class)
-from .._core._compat import DeprecatedAwaitable
-from ..abc import Event, ObjectReceiveStream, ObjectSendStream
-from ..lowlevel import checkpoint
-
-T_Item = TypeVar('T_Item')
-
-
-class MemoryObjectStreamStatistics(NamedTuple):
- current_buffer_used: int #: number of items stored in the buffer
- #: maximum number of items that can be stored on this stream (or :data:`math.inf`)
- max_buffer_size: float
- open_send_streams: int #: number of unclosed clones of the send stream
- open_receive_streams: int #: number of unclosed clones of the receive stream
- tasks_waiting_send: int #: number of tasks blocked on :meth:`MemoryObjectSendStream.send`
- #: number of tasks blocked on :meth:`MemoryObjectReceiveStream.receive`
- tasks_waiting_receive: int
-
-
-@dataclass(eq=False)
-class MemoryObjectStreamState(Generic[T_Item]):
- max_buffer_size: float = field()
- buffer: Deque[T_Item] = field(init=False, default_factory=deque)
- open_send_channels: int = field(init=False, default=0)
- open_receive_channels: int = field(init=False, default=0)
- waiting_receivers: 'OrderedDict[Event, List[T_Item]]' = field(init=False,
- default_factory=OrderedDict)
- waiting_senders: 'OrderedDict[Event, T_Item]' = field(init=False, default_factory=OrderedDict)
-
- def statistics(self) -> MemoryObjectStreamStatistics:
- return MemoryObjectStreamStatistics(
- len(self.buffer), self.max_buffer_size, self.open_send_channels,
- self.open_receive_channels, len(self.waiting_senders), len(self.waiting_receivers))
-
-
-@dataclass(eq=False)
-class MemoryObjectReceiveStream(Generic[T_Item], ObjectReceiveStream[T_Item]):
- _state: MemoryObjectStreamState[T_Item]
- _closed: bool = field(init=False, default=False)
-
- def __post_init__(self) -> None:
- self._state.open_receive_channels += 1
-
- def receive_nowait(self) -> T_Item:
- """
- Receive the next item if it can be done without waiting.
-
- :return: the received item
- :raises ~anyio.ClosedResourceError: if this send stream has been closed
- :raises ~anyio.EndOfStream: if the buffer is empty and this stream has been
- closed from the sending end
- :raises ~anyio.WouldBlock: if there are no items in the buffer and no tasks
- waiting to send
-
- """
- if self._closed:
- raise ClosedResourceError
-
- if self._state.waiting_senders:
- # Get the item from the next sender
- send_event, item = self._state.waiting_senders.popitem(last=False)
- self._state.buffer.append(item)
- send_event.set()
-
- if self._state.buffer:
- return self._state.buffer.popleft()
- elif not self._state.open_send_channels:
- raise EndOfStream
-
- raise WouldBlock
-
- async def receive(self) -> T_Item:
- await checkpoint()
- try:
- return self.receive_nowait()
- except WouldBlock:
- # Add ourselves in the queue
- receive_event = Event()
- container: List[T_Item] = []
- self._state.waiting_receivers[receive_event] = container
-
- try:
- await receive_event.wait()
- except get_cancelled_exc_class():
- # Ignore the immediate cancellation if we already received an item, so as not to
- # lose it
- if not container:
- raise
- finally:
- self._state.waiting_receivers.pop(receive_event, None)
-
- if container:
- return container[0]
- else:
- raise EndOfStream
-
- def clone(self) -> 'MemoryObjectReceiveStream':
- """
- Create a clone of this receive stream.
-
- Each clone can be closed separately. Only when all clones have been closed will the
- receiving end of the memory stream be considered closed by the sending ends.
-
- :return: the cloned stream
-
- """
- if self._closed:
- raise ClosedResourceError
-
- return MemoryObjectReceiveStream(_state=self._state)
-
- def close(self) -> None:
- """
- Close the stream.
-
- This works the exact same way as :meth:`aclose`, but is provided as a special case for the
- benefit of synchronous callbacks.
-
- """
- if not self._closed:
- self._closed = True
- self._state.open_receive_channels -= 1
- if self._state.open_receive_channels == 0:
- send_events = list(self._state.waiting_senders.keys())
- for event in send_events:
- event.set()
-
- async def aclose(self) -> None:
- self.close()
-
- def statistics(self) -> MemoryObjectStreamStatistics:
- """
- Return statistics about the current state of this stream.
-
- .. versionadded:: 3.0
- """
- return self._state.statistics()
-
- def __enter__(self) -> 'MemoryObjectReceiveStream[T_Item]':
- return self
-
- def __exit__(self, exc_type: Optional[Type[BaseException]],
- exc_val: Optional[BaseException],
- exc_tb: Optional[TracebackType]) -> None:
- self.close()
-
-
-@dataclass(eq=False)
-class MemoryObjectSendStream(Generic[T_Item], ObjectSendStream[T_Item]):
- _state: MemoryObjectStreamState[T_Item]
- _closed: bool = field(init=False, default=False)
-
- def __post_init__(self) -> None:
- self._state.open_send_channels += 1
-
- def send_nowait(self, item: T_Item) -> DeprecatedAwaitable:
- """
- Send an item immediately if it can be done without waiting.
-
- :param item: the item to send
- :raises ~anyio.ClosedResourceError: if this send stream has been closed
- :raises ~anyio.BrokenResourceError: if the stream has been closed from the
- receiving end
- :raises ~anyio.WouldBlock: if the buffer is full and there are no tasks waiting
- to receive
-
- """
- if self._closed:
- raise ClosedResourceError
- if not self._state.open_receive_channels:
- raise BrokenResourceError
-
- if self._state.waiting_receivers:
- receive_event, container = self._state.waiting_receivers.popitem(last=False)
- container.append(item)
- receive_event.set()
- elif len(self._state.buffer) < self._state.max_buffer_size:
- self._state.buffer.append(item)
- else:
- raise WouldBlock
-
- return DeprecatedAwaitable(self.send_nowait)
-
- async def send(self, item: T_Item) -> None:
- await checkpoint()
- try:
- self.send_nowait(item)
- except WouldBlock:
- # Wait until there's someone on the receiving end
- send_event = Event()
- self._state.waiting_senders[send_event] = item
- try:
- await send_event.wait()
- except BaseException:
- self._state.waiting_senders.pop(send_event, None) # type: ignore[arg-type]
- raise
-
- if self._state.waiting_senders.pop(send_event, None): # type: ignore[arg-type]
- raise BrokenResourceError
-
- def clone(self) -> 'MemoryObjectSendStream':
- """
- Create a clone of this send stream.
-
- Each clone can be closed separately. Only when all clones have been closed will the
- sending end of the memory stream be considered closed by the receiving ends.
-
- :return: the cloned stream
-
- """
- if self._closed:
- raise ClosedResourceError
-
- return MemoryObjectSendStream(_state=self._state)
-
- def close(self) -> None:
- """
- Close the stream.
-
- This works the exact same way as :meth:`aclose`, but is provided as a special case for the
- benefit of synchronous callbacks.
-
- """
- if not self._closed:
- self._closed = True
- self._state.open_send_channels -= 1
- if self._state.open_send_channels == 0:
- receive_events = list(self._state.waiting_receivers.keys())
- self._state.waiting_receivers.clear()
- for event in receive_events:
- event.set()
-
- async def aclose(self) -> None:
- self.close()
-
- def statistics(self) -> MemoryObjectStreamStatistics:
- """
- Return statistics about the current state of this stream.
-
- .. versionadded:: 3.0
- """
- return self._state.statistics()
-
- def __enter__(self) -> 'MemoryObjectSendStream[T_Item]':
- return self
-
- def __exit__(self, exc_type: Optional[Type[BaseException]],
- exc_val: Optional[BaseException],
- exc_tb: Optional[TracebackType]) -> None:
- self.close()
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/stapled.py b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/stapled.py
deleted file mode 100644
index 0d5e7fb2..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/stapled.py
+++ /dev/null
@@ -1,124 +0,0 @@
-from dataclasses import dataclass
-from typing import Any, Callable, Generic, List, Mapping, Optional, Sequence, TypeVar
-
-from ..abc import (
- ByteReceiveStream, ByteSendStream, ByteStream, Listener, ObjectReceiveStream, ObjectSendStream,
- ObjectStream, TaskGroup)
-
-T_Item = TypeVar('T_Item')
-T_Stream = TypeVar('T_Stream')
-
-
-@dataclass(eq=False)
-class StapledByteStream(ByteStream):
- """
- Combines two byte streams into a single, bidirectional byte stream.
-
- Extra attributes will be provided from both streams, with the receive stream providing the
- values in case of a conflict.
-
- :param ByteSendStream send_stream: the sending byte stream
- :param ByteReceiveStream receive_stream: the receiving byte stream
- """
-
- send_stream: ByteSendStream
- receive_stream: ByteReceiveStream
-
- async def receive(self, max_bytes: int = 65536) -> bytes:
- return await self.receive_stream.receive(max_bytes)
-
- async def send(self, item: bytes) -> None:
- await self.send_stream.send(item)
-
- async def send_eof(self) -> None:
- await self.send_stream.aclose()
-
- async def aclose(self) -> None:
- await self.send_stream.aclose()
- await self.receive_stream.aclose()
-
- @property
- def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
- return {**self.send_stream.extra_attributes, **self.receive_stream.extra_attributes}
-
-
-@dataclass(eq=False)
-class StapledObjectStream(Generic[T_Item], ObjectStream[T_Item]):
- """
- Combines two object streams into a single, bidirectional object stream.
-
- Extra attributes will be provided from both streams, with the receive stream providing the
- values in case of a conflict.
-
- :param ObjectSendStream send_stream: the sending object stream
- :param ObjectReceiveStream receive_stream: the receiving object stream
- """
-
- send_stream: ObjectSendStream[T_Item]
- receive_stream: ObjectReceiveStream[T_Item]
-
- async def receive(self) -> T_Item:
- return await self.receive_stream.receive()
-
- async def send(self, item: T_Item) -> None:
- await self.send_stream.send(item)
-
- async def send_eof(self) -> None:
- await self.send_stream.aclose()
-
- async def aclose(self) -> None:
- await self.send_stream.aclose()
- await self.receive_stream.aclose()
-
- @property
- def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
- return {**self.send_stream.extra_attributes, **self.receive_stream.extra_attributes}
-
-
-@dataclass(eq=False)
-class MultiListener(Generic[T_Stream], Listener[T_Stream]):
- """
- Combines multiple listeners into one, serving connections from all of them at once.
-
- Any MultiListeners in the given collection of listeners will have their listeners moved into
- this one.
-
- Extra attributes are provided from each listener, with each successive listener overriding any
- conflicting attributes from the previous one.
-
- :param listeners: listeners to serve
- :type listeners: Sequence[Listener[T_Stream]]
- """
-
- listeners: Sequence[Listener[T_Stream]]
-
- def __post_init__(self) -> None:
- listeners: List[Listener[T_Stream]] = []
- for listener in self.listeners:
- if isinstance(listener, MultiListener):
- listeners.extend(listener.listeners)
- del listener.listeners[:] # type: ignore[attr-defined]
- else:
- listeners.append(listener)
-
- self.listeners = listeners
-
- async def serve(self, handler: Callable[[T_Stream], Any],
- task_group: Optional[TaskGroup] = None) -> None:
- from .. import create_task_group
-
- async with create_task_group() as tg:
- for listener in self.listeners:
- tg.start_soon(listener.serve, handler, task_group)
-
- async def aclose(self) -> None:
- for listener in self.listeners:
- await listener.aclose()
-
- @property
- def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
- attributes: dict = {}
- for listener in self.listeners:
- attributes.update(listener.extra_attributes)
-
- return attributes
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/text.py b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/text.py
deleted file mode 100644
index d352b5ba..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/text.py
+++ /dev/null
@@ -1,130 +0,0 @@
-import codecs
-from dataclasses import InitVar, dataclass, field
-from typing import Any, Callable, Mapping, Tuple
-
-from ..abc import (
- AnyByteReceiveStream, AnyByteSendStream, AnyByteStream, ObjectReceiveStream, ObjectSendStream,
- ObjectStream)
-
-
-@dataclass(eq=False)
-class TextReceiveStream(ObjectReceiveStream[str]):
- """
- Stream wrapper that decodes bytes to strings using the given encoding.
-
- Decoding is done using :class:`~codecs.IncrementalDecoder` which returns any completely
- received unicode characters as soon as they come in.
-
- :param transport_stream: any bytes-based receive stream
- :param encoding: character encoding to use for decoding bytes to strings (defaults to
- ``utf-8``)
- :param errors: handling scheme for decoding errors (defaults to ``strict``; see the
- `codecs module documentation`_ for a comprehensive list of options)
-
- .. _codecs module documentation: https://docs.python.org/3/library/codecs.html#codec-objects
- """
-
- transport_stream: AnyByteReceiveStream
- encoding: InitVar[str] = 'utf-8'
- errors: InitVar[str] = 'strict'
- _decoder: codecs.IncrementalDecoder = field(init=False)
-
- def __post_init__(self, encoding: str, errors: str) -> None:
- decoder_class = codecs.getincrementaldecoder(encoding)
- self._decoder = decoder_class(errors=errors)
-
- async def receive(self) -> str:
- while True:
- chunk = await self.transport_stream.receive()
- decoded = self._decoder.decode(chunk)
- if decoded:
- return decoded
-
- async def aclose(self) -> None:
- await self.transport_stream.aclose()
- self._decoder.reset()
-
- @property
- def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
- return self.transport_stream.extra_attributes
-
-
-@dataclass(eq=False)
-class TextSendStream(ObjectSendStream[str]):
- """
- Sends strings to the wrapped stream as bytes using the given encoding.
-
- :param AnyByteSendStream transport_stream: any bytes-based send stream
- :param str encoding: character encoding to use for encoding strings to bytes (defaults to
- ``utf-8``)
- :param str errors: handling scheme for encoding errors (defaults to ``strict``; see the
- `codecs module documentation`_ for a comprehensive list of options)
-
- .. _codecs module documentation: https://docs.python.org/3/library/codecs.html#codec-objects
- """
-
- transport_stream: AnyByteSendStream
- encoding: InitVar[str] = 'utf-8'
- errors: str = 'strict'
- _encoder: Callable[..., Tuple[bytes, int]] = field(init=False)
-
- def __post_init__(self, encoding: str) -> None:
- self._encoder = codecs.getencoder(encoding)
-
- async def send(self, item: str) -> None:
- encoded = self._encoder(item, self.errors)[0]
- await self.transport_stream.send(encoded)
-
- async def aclose(self) -> None:
- await self.transport_stream.aclose()
-
- @property
- def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
- return self.transport_stream.extra_attributes
-
-
-@dataclass(eq=False)
-class TextStream(ObjectStream[str]):
- """
- A bidirectional stream that decodes bytes to strings on receive and encodes strings to bytes on
- send.
-
- Extra attributes will be provided from both streams, with the receive stream providing the
- values in case of a conflict.
-
- :param AnyByteStream transport_stream: any bytes-based stream
- :param str encoding: character encoding to use for encoding/decoding strings to/from bytes
- (defaults to ``utf-8``)
- :param str errors: handling scheme for encoding errors (defaults to ``strict``; see the
- `codecs module documentation`_ for a comprehensive list of options)
-
- .. _codecs module documentation: https://docs.python.org/3/library/codecs.html#codec-objects
- """
-
- transport_stream: AnyByteStream
- encoding: InitVar[str] = 'utf-8'
- errors: InitVar[str] = 'strict'
- _receive_stream: TextReceiveStream = field(init=False)
- _send_stream: TextSendStream = field(init=False)
-
- def __post_init__(self, encoding: str, errors: str) -> None:
- self._receive_stream = TextReceiveStream(self.transport_stream, encoding=encoding,
- errors=errors)
- self._send_stream = TextSendStream(self.transport_stream, encoding=encoding, errors=errors)
-
- async def receive(self) -> str:
- return await self._receive_stream.receive()
-
- async def send(self, item: str) -> None:
- await self._send_stream.send(item)
-
- async def send_eof(self) -> None:
- await self.transport_stream.send_eof()
-
- async def aclose(self) -> None:
- await self._send_stream.aclose()
- await self._receive_stream.aclose()
-
- @property
- def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
- return {**self._send_stream.extra_attributes, **self._receive_stream.extra_attributes}
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/tls.py b/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/tls.py
deleted file mode 100644
index 1d84c05c..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/anyio/streams/tls.py
+++ /dev/null
@@ -1,262 +0,0 @@
-import logging
-import re
-import ssl
-from dataclasses import dataclass
-from functools import wraps
-from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple, TypeVar, Union
-
-from .. import BrokenResourceError, EndOfStream, aclose_forcefully, get_cancelled_exc_class
-from .._core._typedattr import TypedAttributeSet, typed_attribute
-from ..abc import AnyByteStream, ByteStream, Listener, TaskGroup
-
-T_Retval = TypeVar('T_Retval')
-
-
-class TLSAttribute(TypedAttributeSet):
- """Contains Transport Layer Security related attributes."""
- #: the selected ALPN protocol
- alpn_protocol: Optional[str] = typed_attribute()
- #: the channel binding for type ``tls-unique``
- channel_binding_tls_unique: bytes = typed_attribute()
- #: the selected cipher
- cipher: Tuple[str, str, int] = typed_attribute()
- #: the peer certificate in dictionary form (see :meth:`ssl.SSLSocket.getpeercert` for more
- #: information)
- peer_certificate: Optional[Dict[str, Union[str, tuple]]] = typed_attribute()
- #: the peer certificate in binary form
- peer_certificate_binary: Optional[bytes] = typed_attribute()
- #: ``True`` if this is the server side of the connection
- server_side: bool = typed_attribute()
- #: ciphers shared between both ends of the TLS connection
- shared_ciphers: List[Tuple[str, str, int]] = typed_attribute()
- #: the :class:`~ssl.SSLObject` used for encryption
- ssl_object: ssl.SSLObject = typed_attribute()
- #: ``True`` if this stream does (and expects) a closing TLS handshake when the stream is being
- #: closed
- standard_compatible: bool = typed_attribute()
- #: the TLS protocol version (e.g. ``TLSv1.2``)
- tls_version: str = typed_attribute()
-
-
-@dataclass(eq=False)
-class TLSStream(ByteStream):
- """
- A stream wrapper that encrypts all sent data and decrypts received data.
-
- This class has no public initializer; use :meth:`wrap` instead.
- All extra attributes from :class:`~TLSAttribute` are supported.
-
- :var AnyByteStream transport_stream: the wrapped stream
-
- """
- transport_stream: AnyByteStream
- standard_compatible: bool
- _ssl_object: ssl.SSLObject
- _read_bio: ssl.MemoryBIO
- _write_bio: ssl.MemoryBIO
-
- @classmethod
- async def wrap(cls, transport_stream: AnyByteStream, *, server_side: Optional[bool] = None,
- hostname: Optional[str] = None, ssl_context: Optional[ssl.SSLContext] = None,
- standard_compatible: bool = True) -> 'TLSStream':
- """
- Wrap an existing stream with Transport Layer Security.
-
- This performs a TLS handshake with the peer.
-
- :param transport_stream: a bytes-transporting stream to wrap
- :param server_side: ``True`` if this is the server side of the connection, ``False`` if
- this is the client side (if omitted, will be set to ``False`` if ``hostname`` has been
- provided, ``False`` otherwise). Used only to create a default context when an explicit
- context has not been provided.
- :param hostname: host name of the peer (if host name checking is desired)
- :param ssl_context: the SSLContext object to use (if not provided, a secure default will be
- created)
- :param standard_compatible: if ``False``, skip the closing handshake when closing the
- connection, and don't raise an exception if the peer does the same
- :raises ~ssl.SSLError: if the TLS handshake fails
-
- """
- if server_side is None:
- server_side = not hostname
-
- if not ssl_context:
- purpose = ssl.Purpose.CLIENT_AUTH if server_side else ssl.Purpose.SERVER_AUTH
- ssl_context = ssl.create_default_context(purpose)
-
- bio_in = ssl.MemoryBIO()
- bio_out = ssl.MemoryBIO()
- ssl_object = ssl_context.wrap_bio(bio_in, bio_out, server_side=server_side,
- server_hostname=hostname)
- wrapper = cls(transport_stream=transport_stream,
- standard_compatible=standard_compatible, _ssl_object=ssl_object,
- _read_bio=bio_in, _write_bio=bio_out)
- await wrapper._call_sslobject_method(ssl_object.do_handshake)
- return wrapper
-
- async def _call_sslobject_method(
- self, func: Callable[..., T_Retval], *args: object
- ) -> T_Retval:
- while True:
- try:
- result = func(*args)
- except ssl.SSLWantReadError:
- try:
- # Flush any pending writes first
- if self._write_bio.pending:
- await self.transport_stream.send(self._write_bio.read())
-
- data = await self.transport_stream.receive()
- except EndOfStream:
- self._read_bio.write_eof()
- except OSError as exc:
- self._read_bio.write_eof()
- self._write_bio.write_eof()
- raise BrokenResourceError from exc
- else:
- self._read_bio.write(data)
- except ssl.SSLWantWriteError:
- await self.transport_stream.send(self._write_bio.read())
- except (ssl.SSLEOFError, ssl.SSLSyscallError) as exc:
- raise BrokenResourceError from exc
- else:
- # Flush any pending writes first
- if self._write_bio.pending:
- await self.transport_stream.send(self._write_bio.read())
-
- return result
-
- async def unwrap(self) -> Tuple[AnyByteStream, bytes]:
- """
- Does the TLS closing handshake.
-
- :return: a tuple of (wrapped byte stream, bytes left in the read buffer)
-
- """
- await self._call_sslobject_method(self._ssl_object.unwrap)
- self._read_bio.write_eof()
- self._write_bio.write_eof()
- return self.transport_stream, self._read_bio.read()
-
- async def aclose(self) -> None:
- if self.standard_compatible:
- try:
- await self.unwrap()
- except BaseException:
- await aclose_forcefully(self.transport_stream)
- raise
-
- await self.transport_stream.aclose()
-
- async def receive(self, max_bytes: int = 65536) -> bytes:
- data = await self._call_sslobject_method(self._ssl_object.read, max_bytes)
- if not data:
- raise EndOfStream
-
- return data
-
- async def send(self, item: bytes) -> None:
- await self._call_sslobject_method(self._ssl_object.write, item)
-
- async def send_eof(self) -> None:
- tls_version = self.extra(TLSAttribute.tls_version)
- match = re.match(r'TLSv(\d+)(?:\.(\d+))?', tls_version)
- if match:
- major, minor = int(match.group(1)), int(match.group(2) or 0)
- if (major, minor) < (1, 3):
- raise NotImplementedError(f'send_eof() requires at least TLSv1.3; current '
- f'session uses {tls_version}')
-
- raise NotImplementedError('send_eof() has not yet been implemented for TLS streams')
-
- @property
- def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
- return {
- **self.transport_stream.extra_attributes,
- TLSAttribute.alpn_protocol: self._ssl_object.selected_alpn_protocol,
- TLSAttribute.channel_binding_tls_unique: self._ssl_object.get_channel_binding,
- TLSAttribute.cipher: self._ssl_object.cipher,
- TLSAttribute.peer_certificate: lambda: self._ssl_object.getpeercert(False),
- TLSAttribute.peer_certificate_binary: lambda: self._ssl_object.getpeercert(True),
- TLSAttribute.server_side: lambda: self._ssl_object.server_side,
- TLSAttribute.shared_ciphers: lambda: self._ssl_object.shared_ciphers(),
- TLSAttribute.standard_compatible: lambda: self.standard_compatible,
- TLSAttribute.ssl_object: lambda: self._ssl_object,
- TLSAttribute.tls_version: self._ssl_object.version
- }
-
-
-@dataclass(eq=False)
-class TLSListener(Listener[TLSStream]):
- """
- A convenience listener that wraps another listener and auto-negotiates a TLS session on every
- accepted connection.
-
- If the TLS handshake times out or raises an exception, :meth:`handle_handshake_error` is
- called to do whatever post-mortem processing is deemed necessary.
-
- Supports only the :attr:`~TLSAttribute.standard_compatible` extra attribute.
-
- :param Listener listener: the listener to wrap
- :param ssl_context: the SSL context object
- :param standard_compatible: a flag passed through to :meth:`TLSStream.wrap`
- :param handshake_timeout: time limit for the TLS handshake
- (passed to :func:`~anyio.fail_after`)
- """
-
- listener: Listener
- ssl_context: ssl.SSLContext
- standard_compatible: bool = True
- handshake_timeout: float = 30
-
- @staticmethod
- async def handle_handshake_error(exc: BaseException, stream: AnyByteStream) -> None:
- f"""
- Handle an exception raised during the TLS handshake.
-
- This method does 3 things:
-
- #. Forcefully closes the original stream
- #. Logs the exception (unless it was a cancellation exception) using the ``{__name__}``
- logger
- #. Reraises the exception if it was a base exception or a cancellation exception
-
- :param exc: the exception
- :param stream: the original stream
-
- """
- await aclose_forcefully(stream)
-
- # Log all except cancellation exceptions
- if not isinstance(exc, get_cancelled_exc_class()):
- logging.getLogger(__name__).exception('Error during TLS handshake')
-
- # Only reraise base exceptions and cancellation exceptions
- if not isinstance(exc, Exception) or isinstance(exc, get_cancelled_exc_class()):
- raise
-
- async def serve(self, handler: Callable[[TLSStream], Any],
- task_group: Optional[TaskGroup] = None) -> None:
- @wraps(handler)
- async def handler_wrapper(stream: AnyByteStream) -> None:
- from .. import fail_after
- try:
- with fail_after(self.handshake_timeout):
- wrapped_stream = await TLSStream.wrap(
- stream, ssl_context=self.ssl_context,
- standard_compatible=self.standard_compatible)
- except BaseException as exc:
- await self.handle_handshake_error(exc, stream)
- else:
- await handler(wrapped_stream)
-
- await self.listener.serve(handler_wrapper, task_group)
-
- async def aclose(self) -> None:
- await self.listener.aclose()
-
- @property
- def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
- return {
- TLSAttribute.standard_compatible: lambda: self.standard_compatible,
- }
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/to_process.py b/IKEA_scraper/.venv/Lib/site-packages/anyio/to_process.py
deleted file mode 100644
index 5675e9eb..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/anyio/to_process.py
+++ /dev/null
@@ -1,230 +0,0 @@
-import os
-import pickle
-import subprocess
-import sys
-from collections import deque
-from importlib.abc import Loader
-from importlib.util import module_from_spec, spec_from_file_location
-from typing import Callable, Deque, List, Optional, Set, Tuple, TypeVar, cast
-
-from ._core._eventloop import current_time, get_asynclib, get_cancelled_exc_class
-from ._core._exceptions import BrokenWorkerProcess
-from ._core._subprocesses import open_process
-from ._core._synchronization import CapacityLimiter
-from ._core._tasks import CancelScope, fail_after
-from .abc import ByteReceiveStream, ByteSendStream, Process
-from .lowlevel import RunVar, checkpoint_if_cancelled
-from .streams.buffered import BufferedByteReceiveStream
-
-WORKER_MAX_IDLE_TIME = 300 # 5 minutes
-
-T_Retval = TypeVar('T_Retval')
-_process_pool_workers: RunVar[Set[Process]] = RunVar('_process_pool_workers')
-_process_pool_idle_workers: RunVar[Deque[Tuple[Process, float]]] = RunVar(
- '_process_pool_idle_workers')
-_default_process_limiter: RunVar[CapacityLimiter] = RunVar('_default_process_limiter')
-
-
-async def run_sync(
- func: Callable[..., T_Retval], *args: object, cancellable: bool = False,
- limiter: Optional[CapacityLimiter] = None) -> T_Retval:
- """
- Call the given function with the given arguments in a worker process.
-
- If the ``cancellable`` option is enabled and the task waiting for its completion is cancelled,
- the worker process running it will be abruptly terminated using SIGKILL (or
- ``terminateProcess()`` on Windows).
-
- :param func: a callable
- :param args: positional arguments for the callable
- :param cancellable: ``True`` to allow cancellation of the operation while it's running
- :param limiter: capacity limiter to use to limit the total amount of processes running
- (if omitted, the default limiter is used)
- :return: an awaitable that yields the return value of the function.
-
- """
- async def send_raw_command(pickled_cmd: bytes) -> object:
- try:
- await stdin.send(pickled_cmd)
- response = await buffered.receive_until(b'\n', 50)
- status, length = response.split(b' ')
- if status not in (b'RETURN', b'EXCEPTION'):
- raise RuntimeError(f'Worker process returned unexpected response: {response!r}')
-
- pickled_response = await buffered.receive_exactly(int(length))
- except BaseException as exc:
- workers.discard(process)
- try:
- process.kill()
- with CancelScope(shield=True):
- await process.aclose()
- except ProcessLookupError:
- pass
-
- if isinstance(exc, get_cancelled_exc_class()):
- raise
- else:
- raise BrokenWorkerProcess from exc
-
- retval = pickle.loads(pickled_response)
- if status == b'EXCEPTION':
- assert isinstance(retval, BaseException)
- raise retval
- else:
- return retval
-
- # First pickle the request before trying to reserve a worker process
- await checkpoint_if_cancelled()
- request = pickle.dumps(('run', func, args), protocol=pickle.HIGHEST_PROTOCOL)
-
- # If this is the first run in this event loop thread, set up the necessary variables
- try:
- workers = _process_pool_workers.get()
- idle_workers = _process_pool_idle_workers.get()
- except LookupError:
- workers = set()
- idle_workers = deque()
- _process_pool_workers.set(workers)
- _process_pool_idle_workers.set(idle_workers)
- get_asynclib().setup_process_pool_exit_at_shutdown(workers)
-
- async with (limiter or current_default_process_limiter()):
- # Pop processes from the pool (starting from the most recently used) until we find one that
- # hasn't exited yet
- process: Process
- while idle_workers:
- process, idle_since = idle_workers.pop()
- if process.returncode is None:
- stdin = cast(ByteSendStream, process.stdin)
- buffered = BufferedByteReceiveStream(cast(ByteReceiveStream, process.stdout))
-
- # Prune any other workers that have been idle for WORKER_MAX_IDLE_TIME seconds or
- # longer
- now = current_time()
- killed_processes: List[Process] = []
- while idle_workers:
- if now - idle_workers[0][1] < WORKER_MAX_IDLE_TIME:
- break
-
- process, idle_since = idle_workers.popleft()
- process.kill()
- workers.remove(process)
- killed_processes.append(process)
-
- with CancelScope(shield=True):
- for process in killed_processes:
- await process.aclose()
-
- break
-
- workers.remove(process)
- else:
- command = [sys.executable, '-u', '-m', __name__]
- process = await open_process(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
- try:
- stdin = cast(ByteSendStream, process.stdin)
- buffered = BufferedByteReceiveStream(cast(ByteReceiveStream, process.stdout))
- with fail_after(20):
- message = await buffered.receive(6)
-
- if message != b'READY\n':
- raise BrokenWorkerProcess(
- f'Worker process returned unexpected response: {message!r}')
-
- main_module_path = getattr(sys.modules['__main__'], '__file__', None)
- pickled = pickle.dumps(('init', sys.path, main_module_path),
- protocol=pickle.HIGHEST_PROTOCOL)
- await send_raw_command(pickled)
- except (BrokenWorkerProcess, get_cancelled_exc_class()):
- raise
- except BaseException as exc:
- process.kill()
- raise BrokenWorkerProcess('Error during worker process initialization') from exc
-
- workers.add(process)
-
- with CancelScope(shield=not cancellable):
- try:
- return cast(T_Retval, await send_raw_command(request))
- finally:
- if process in workers:
- idle_workers.append((process, current_time()))
-
-
-def current_default_process_limiter() -> CapacityLimiter:
- """
- Return the capacity limiter that is used by default to limit the number of worker processes.
-
- :return: a capacity limiter object
-
- """
- try:
- return _default_process_limiter.get()
- except LookupError:
- limiter = CapacityLimiter(os.cpu_count() or 2)
- _default_process_limiter.set(limiter)
- return limiter
-
-
-def process_worker() -> None:
- # Redirect standard streams to os.devnull so that user code won't interfere with the
- # parent-worker communication
- stdin = sys.stdin
- stdout = sys.stdout
- sys.stdin = open(os.devnull)
- sys.stdout = open(os.devnull, 'w')
-
- stdout.buffer.write(b'READY\n')
- while True:
- retval = exception = None
- try:
- command, *args = pickle.load(stdin.buffer)
- except EOFError:
- return
- except BaseException as exc:
- exception = exc
- else:
- if command == 'run':
- func, args = args
- try:
- retval = func(*args)
- except BaseException as exc:
- exception = exc
- elif command == 'init':
- main_module_path: Optional[str]
- sys.path, main_module_path = args
- del sys.modules['__main__']
- if main_module_path:
- # Load the parent's main module but as __mp_main__ instead of __main__
- # (like multiprocessing does) to avoid infinite recursion
- try:
- spec = spec_from_file_location('__mp_main__', main_module_path)
- if spec and spec.loader:
- main = module_from_spec(spec)
- cast(Loader, spec.loader).exec_module(main)
- sys.modules['__main__'] = main
- except BaseException as exc:
- exception = exc
-
- try:
- if exception is not None:
- status = b'EXCEPTION'
- pickled = pickle.dumps(exception, pickle.HIGHEST_PROTOCOL)
- else:
- status = b'RETURN'
- pickled = pickle.dumps(retval, pickle.HIGHEST_PROTOCOL)
- except BaseException as exc:
- exception = exc
- status = b'EXCEPTION'
- pickled = pickle.dumps(exc, pickle.HIGHEST_PROTOCOL)
-
- stdout.buffer.write(b'%s %d\n' % (status, len(pickled)))
- stdout.buffer.write(pickled)
-
- # Respect SIGTERM
- if isinstance(exception, SystemExit):
- raise exception
-
-
-if __name__ == '__main__':
- process_worker()
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio/to_thread.py b/IKEA_scraper/.venv/Lib/site-packages/anyio/to_thread.py
deleted file mode 100644
index 5fc95894..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/anyio/to_thread.py
+++ /dev/null
@@ -1,54 +0,0 @@
-from typing import Callable, Optional, TypeVar
-from warnings import warn
-
-from ._core._eventloop import get_asynclib
-from .abc import CapacityLimiter
-
-T_Retval = TypeVar('T_Retval')
-
-
-async def run_sync(
- func: Callable[..., T_Retval], *args: object, cancellable: bool = False,
- limiter: Optional[CapacityLimiter] = None) -> T_Retval:
- """
- Call the given function with the given arguments in a worker thread.
-
- If the ``cancellable`` option is enabled and the task waiting for its completion is cancelled,
- the thread will still run its course but its return value (or any raised exception) will be
- ignored.
-
- :param func: a callable
- :param args: positional arguments for the callable
- :param cancellable: ``True`` to allow cancellation of the operation
- :param limiter: capacity limiter to use to limit the total amount of threads running
- (if omitted, the default limiter is used)
- :return: an awaitable that yields the return value of the function.
-
- """
- return await get_asynclib().run_sync_in_worker_thread(func, *args, cancellable=cancellable,
- limiter=limiter)
-
-
-async def run_sync_in_worker_thread(
- func: Callable[..., T_Retval], *args: object, cancellable: bool = False,
- limiter: Optional[CapacityLimiter] = None) -> T_Retval:
- warn('run_sync_in_worker_thread() has been deprecated, use anyio.to_thread.run_sync() instead',
- DeprecationWarning)
- return await run_sync(func, *args, cancellable=cancellable, limiter=limiter)
-
-
-def current_default_thread_limiter() -> CapacityLimiter:
- """
- Return the capacity limiter that is used by default to limit the number of concurrent threads.
-
- :return: a capacity limiter object
-
- """
- return get_asynclib().current_default_thread_limiter()
-
-
-def current_default_worker_thread_limiter() -> CapacityLimiter:
- warn('current_default_worker_thread_limiter() has been deprecated, '
- 'use anyio.to_thread.current_default_thread_limiter() instead',
- DeprecationWarning)
- return current_default_thread_limiter()
diff --git a/IKEA_scraper/.venv/Lib/site-packages/bottle_websocket/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/bottle_websocket/__pycache__/__init__.cpython-39.pyc
index 69519787..ae52f6c5 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/bottle_websocket/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/bottle_websocket/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/bottle_websocket/__pycache__/plugin.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/bottle_websocket/__pycache__/plugin.cpython-39.pyc
index f3aa39a0..93be116d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/bottle_websocket/__pycache__/plugin.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/bottle_websocket/__pycache__/plugin.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/bottle_websocket/__pycache__/server.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/bottle_websocket/__pycache__/server.cpython-39.pyc
index d1b105e3..8db352db 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/bottle_websocket/__pycache__/server.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/bottle_websocket/__pycache__/server.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/__init__.cpython-39.pyc
index f0b85e05..3df043c1 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/dammit.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/dammit.cpython-39.pyc
index 803f2d65..26cf34e8 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/dammit.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/dammit.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/diagnose.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/diagnose.cpython-39.pyc
index 9ecdbcf0..09a338e5 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/diagnose.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/diagnose.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/element.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/element.cpython-39.pyc
index 9c92b03d..3a456169 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/element.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/element.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/formatter.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/formatter.cpython-39.pyc
index f68e6b1f..462d19e4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/formatter.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/formatter.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/testing.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/testing.cpython-39.pyc
index 746e173c..d18a444c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/testing.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/bs4/__pycache__/testing.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/__init__.cpython-39.pyc
index 2c13aac0..59032c9e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/_html5lib.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/_html5lib.cpython-39.pyc
index a8544610..9fb11ddc 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/_html5lib.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/_html5lib.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/_htmlparser.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/_htmlparser.cpython-39.pyc
index 17e41583..aed8245e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/_htmlparser.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/_htmlparser.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/_lxml.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/_lxml.cpython-39.pyc
index da010c65..08896a90 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/_lxml.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/bs4/builder/__pycache__/_lxml.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/certifi/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/certifi/__pycache__/__init__.cpython-39.pyc
index 76977a0e..6f3f627a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/certifi/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/certifi/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/certifi/__pycache__/__main__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/certifi/__pycache__/__main__.cpython-39.pyc
index b1ac9913..918e28da 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/certifi/__pycache__/__main__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/certifi/__pycache__/__main__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/certifi/__pycache__/core.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/certifi/__pycache__/core.cpython-39.pyc
index 8c9635a1..07964ddd 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/certifi/__pycache__/core.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/certifi/__pycache__/core.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/__init__.cpython-39.pyc
index 8dfe995f..f859cacf 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/api.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/api.cpython-39.pyc
index 6c9eae69..db1578d1 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/api.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/api.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/backend_ctypes.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/backend_ctypes.cpython-39.pyc
index 6e61757f..b0761eed 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/backend_ctypes.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/backend_ctypes.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/cffi_opcode.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/cffi_opcode.cpython-39.pyc
index e2c60193..4ec29a8d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/cffi_opcode.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/cffi_opcode.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/commontypes.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/commontypes.cpython-39.pyc
index 169689d2..15d62dc8 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/commontypes.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/commontypes.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/cparser.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/cparser.cpython-39.pyc
index 7abdf69d..f898be99 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/cparser.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/cparser.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/error.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/error.cpython-39.pyc
index f3f040bb..77fac51e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/error.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/error.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/ffiplatform.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/ffiplatform.cpython-39.pyc
index c6e54229..9b694c4f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/ffiplatform.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/ffiplatform.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/lock.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/lock.cpython-39.pyc
index b137e5d7..842cf51e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/lock.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/lock.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/model.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/model.cpython-39.pyc
index 2e47a0bd..a953c772 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/model.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/model.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/pkgconfig.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/pkgconfig.cpython-39.pyc
index 0dc91d45..70033ec8 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/pkgconfig.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/pkgconfig.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/recompiler.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/recompiler.cpython-39.pyc
index 378fb651..ed1772c2 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/recompiler.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/recompiler.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/setuptools_ext.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/setuptools_ext.cpython-39.pyc
index 9f3bbcf0..5cb1a097 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/setuptools_ext.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/setuptools_ext.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/vengine_cpy.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/vengine_cpy.cpython-39.pyc
index ec22b3a0..83ef1b5d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/vengine_cpy.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/vengine_cpy.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/vengine_gen.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/vengine_gen.cpython-39.pyc
index 1022c259..e272afc8 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/vengine_gen.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/vengine_gen.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/verifier.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/verifier.cpython-39.pyc
index c006306b..561288ea 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/verifier.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/cffi/__pycache__/verifier.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/INSTALLER b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/INSTALLER
deleted file mode 100644
index a1b589e3..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/RECORD b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/RECORD
deleted file mode 100644
index 4bc537c3..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/RECORD
+++ /dev/null
@@ -1,33 +0,0 @@
-../../Scripts/normalizer.exe,sha256=Tb318KAuAZX3JDK2YH_V8XoRI9mah1OfwjHG0lkxy0k,106395
-charset_normalizer-2.0.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-charset_normalizer-2.0.4.dist-info/LICENSE,sha256=6zGgxaT7Cbik4yBV0lweX5w1iidS_vPNcgIT0cz-4kE,1070
-charset_normalizer-2.0.4.dist-info/METADATA,sha256=iGaSYKAbW7dltLfO_sIm347XsC5kqKiFrvR3IHolDio,11710
-charset_normalizer-2.0.4.dist-info/RECORD,,
-charset_normalizer-2.0.4.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
-charset_normalizer-2.0.4.dist-info/entry_points.txt,sha256=5AJq_EPtGGUwJPgQLnBZfbVr-FYCIwT0xP7dIEZO3NI,77
-charset_normalizer-2.0.4.dist-info/top_level.txt,sha256=7ASyzePr8_xuZWJsnqJjIBtyV8vhEo0wBCv1MPRRi3Q,19
-charset_normalizer/__init__.py,sha256=i8FSr9cSFIrh16O8h7com8LKtd9nsLY8wiEhSpk1_aY,1673
-charset_normalizer/__pycache__/__init__.cpython-39.pyc,,
-charset_normalizer/__pycache__/api.cpython-39.pyc,,
-charset_normalizer/__pycache__/cd.cpython-39.pyc,,
-charset_normalizer/__pycache__/constant.cpython-39.pyc,,
-charset_normalizer/__pycache__/legacy.cpython-39.pyc,,
-charset_normalizer/__pycache__/md.cpython-39.pyc,,
-charset_normalizer/__pycache__/models.cpython-39.pyc,,
-charset_normalizer/__pycache__/utils.cpython-39.pyc,,
-charset_normalizer/__pycache__/version.cpython-39.pyc,,
-charset_normalizer/api.py,sha256=xf9L11VEFJGNcGFSS0uJxZwfdPYy87ya97AiVhNr-MI,15685
-charset_normalizer/assets/__init__.py,sha256=JTs2XX9qbYSBhS4EEI93IqEOnzzrHhVmX1gp2vKjZdE,6938
-charset_normalizer/assets/__pycache__/__init__.cpython-39.pyc,,
-charset_normalizer/cd.py,sha256=_HLe1wgJAifJPl4dlBctjK5yIvH0ja67jePw36OrrhI,9223
-charset_normalizer/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-charset_normalizer/cli/__pycache__/__init__.cpython-39.pyc,,
-charset_normalizer/cli/__pycache__/normalizer.cpython-39.pyc,,
-charset_normalizer/cli/normalizer.py,sha256=HWLuXnvQa12U2zjv6rXtashpNnxgxN3lZtVCnqHjIFQ,8334
-charset_normalizer/constant.py,sha256=YA8r7rNGeuLlnqWLs2MISs1mFLmaralG99HNevHuUuo,18390
-charset_normalizer/legacy.py,sha256=L3Qn-DSLjRQoYPtDIzkXcqO0mnFIjOl0p6-gAzYUY54,1640
-charset_normalizer/md.py,sha256=m3XxcK9UhsSCFK7-vrOU6ztn-eaeXKLuS8dP88QY6zw,16263
-charset_normalizer/models.py,sha256=A7vIN3PCJuM1KMDBs60Gb_WF1lHJsrkrllc5_RMV-nE,12541
-charset_normalizer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-charset_normalizer/utils.py,sha256=0HasZ3NtRNiw-HbmgnfhSDySJfSa4pO4azPTn6vOrP0,8594
-charset_normalizer/version.py,sha256=Ou-PvSj5-2Ci5Y_bH8OSoXrbbTt8sxO5pJRagAzlYUc,79
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/WHEEL b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/WHEEL
deleted file mode 100644
index 385faab0..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/WHEEL
+++ /dev/null
@@ -1,5 +0,0 @@
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.36.2)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
diff --git a/IKEA_scraper/.venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/INSTALLER
similarity index 100%
rename from IKEA_scraper/.venv/Lib/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER
rename to IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/INSTALLER
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/LICENSE b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/LICENSE
similarity index 100%
rename from IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/LICENSE
rename to IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/LICENSE
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/METADATA b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/METADATA
similarity index 89%
rename from IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/METADATA
rename to IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/METADATA
index 794282b1..94cc938c 100644
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/METADATA
+++ b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/METADATA
@@ -1,11 +1,13 @@
Metadata-Version: 2.1
Name: charset-normalizer
-Version: 2.0.4
+Version: 2.0.6
Summary: The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet.
Home-page: https://github.com/ousret/charset_normalizer
Author: Ahmed TAHRI @Ousret
Author-email: ahmed.tahri@cloudnursery.dev
License: MIT
+Project-URL: Bug Reports, https://github.com/Ousret/charset_normalizer/issues
+Project-URL: Documentation, https://charset-normalizer.readthedocs.io/en/latest
Keywords: encoding,i18n,txt,text,charset,charset-detector,normalization,unicode,chardet
Platform: UNKNOWN
Classifier: License :: OSI Approved :: MIT License
@@ -23,8 +25,10 @@ Classifier: Programming Language :: Python :: 3.10
Classifier: Topic :: Text Processing :: Linguistic
Classifier: Topic :: Utilities
Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Typing :: Typed
Requires-Python: >=3.5.0
Description-Content-Type: text/markdown
+License-File: LICENSE
Provides-Extra: unicode_backport
Requires-Dist: unicodedata2 ; extra == 'unicode_backport'
@@ -33,21 +37,12 @@ Requires-Dist: unicodedata2 ; extra == 'unicode_backport'
The Real First Universal Charset Detector
-
-
-
-
-
-
-
-
-
@@ -80,19 +75,23 @@ This project offers you an alternative to **Universal Charset Encoding Detector*
*\*\* : They are clearly using specific code for a specific encoding even if covering most of used one*
+## ⭐ Your support
+
+*Fork, test-it, star-it, submit your ideas! We do listen.*
+
## ⚡ Performance
This package offer better performance than its counterpart Chardet. Here are some numbers.
| Package | Accuracy | Mean per file (ns) | File per sec (est) |
| ------------- | :-------------: | :------------------: | :------------------: |
-| [chardet](https://github.com/chardet/chardet) | 93.0 % | 150 ms | 7 file/sec |
-| charset-normalizer | **95.0 %** | **36 ms** | 28 file/sec |
+| [chardet](https://github.com/chardet/chardet) | 92.0 % | 220 ms | 5 file/sec |
+| charset-normalizer | **97.0 %** | **40 ms** | 25 file/sec |
| Package | 99th percentile | 95th percentile | 50th percentile |
| ------------- | :-------------: | :------------------: | :------------------: |
-| [chardet](https://github.com/chardet/chardet) | 647 ms | 250 ms | 24 ms |
-| charset-normalizer | 354 ms | 202 ms | 16 ms |
+| [chardet](https://github.com/chardet/chardet) | 888 ms | 300 ms | 27 ms |
+| charset-normalizer | 430 ms | 220 ms | 18 ms |
Chardet's performance on larger file (1MB+) are very poor. Expect huge difference on large payload.
@@ -102,24 +101,16 @@ Chardet's performance on larger file (1MB+) are very poor. Expect huge differenc
[cchardet](https://github.com/PyYoshi/cChardet) is a non-native (cpp binding) faster alternative. If speed is the most important factor,
you should try it.
-## Your support
-
-Please ⭐ this repository if this project helped you!
-
## ✨ Installation
Using PyPi for latest stable
```sh
-pip install charset-normalizer
-```
-Or directly from dev-master for latest preview
-```sh
-pip install git+https://github.com/Ousret/charset_normalizer.git
+pip install charset-normalizer -U
```
If you want a more up-to-date `unicodedata` than the one available in your Python setup.
```sh
-pip install charset-normalizer[unicode_backport]
+pip install charset-normalizer[unicode_backport] -U
```
## 🚀 Basic Usage
@@ -243,7 +234,7 @@ Don't confuse package **ftfy** with charset-normalizer or chardet. ftfy goal is
- Discard all charset encoding table that could not fit the binary content.
- Measure chaos, or the mess once opened (by chunks) with a corresponding charset encoding.
- Extract matches with the lowest mess detected.
- - Finally, if there is too much match left, we measure coherence.
+ - Finally, we measure coherence / probe for a language.
**Wait a minute**, what is chaos/mess and coherence according to **YOU ?**
@@ -270,6 +261,6 @@ Feel free to check [issues page](https://github.com/ousret/charset_normalizer/is
Copyright © 2019 [Ahmed TAHRI @Ousret](https://github.com/Ousret).
This project is [MIT](https://github.com/Ousret/charset_normalizer/blob/master/LICENSE) licensed.
-Characters frequencies used in this project © 2012 [Denny Vrandečić](http://denny.vrandecic.de)
+Characters frequencies used in this project © 2012 [Denny Vrandečić](http://simia.net/letters/)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/RECORD b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/RECORD
new file mode 100644
index 00000000..fb4f8675
--- /dev/null
+++ b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/RECORD
@@ -0,0 +1,33 @@
+../../Scripts/normalizer.exe,sha256=E_XOYvCQJ9_b0Ik2oaeZ7MIEqGafDrd7m41LRRMYRng,106395
+charset_normalizer-2.0.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+charset_normalizer-2.0.6.dist-info/LICENSE,sha256=6zGgxaT7Cbik4yBV0lweX5w1iidS_vPNcgIT0cz-4kE,1070
+charset_normalizer-2.0.6.dist-info/METADATA,sha256=zWnIKeYWdCmL4F61s9ci-Q4mjCKzbioT18dp-T0gEag,11241
+charset_normalizer-2.0.6.dist-info/RECORD,,
+charset_normalizer-2.0.6.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
+charset_normalizer-2.0.6.dist-info/entry_points.txt,sha256=5AJq_EPtGGUwJPgQLnBZfbVr-FYCIwT0xP7dIEZO3NI,77
+charset_normalizer-2.0.6.dist-info/top_level.txt,sha256=7ASyzePr8_xuZWJsnqJjIBtyV8vhEo0wBCv1MPRRi3Q,19
+charset_normalizer/__init__.py,sha256=zPNiH2V-xY-kLTkpOfzCIUmf4tly8t7tg7hkgMW7528,1467
+charset_normalizer/__pycache__/__init__.cpython-39.pyc,,
+charset_normalizer/__pycache__/api.cpython-39.pyc,,
+charset_normalizer/__pycache__/cd.cpython-39.pyc,,
+charset_normalizer/__pycache__/constant.cpython-39.pyc,,
+charset_normalizer/__pycache__/legacy.cpython-39.pyc,,
+charset_normalizer/__pycache__/md.cpython-39.pyc,,
+charset_normalizer/__pycache__/models.cpython-39.pyc,,
+charset_normalizer/__pycache__/utils.cpython-39.pyc,,
+charset_normalizer/__pycache__/version.cpython-39.pyc,,
+charset_normalizer/api.py,sha256=fpxRxVAXxeaRXACxYl8EhXfAoybWkaorvCM3kYPv-gU,17738
+charset_normalizer/assets/__init__.py,sha256=wY6V36jmSumayW1HFQNbD19mxuT9AhcUUzl4LQN07Vk,25069
+charset_normalizer/assets/__pycache__/__init__.cpython-39.pyc,,
+charset_normalizer/cd.py,sha256=tfM5s7iwdy7Mr-cLbv4l9hZbs0RnsW3-BaKuJRyUjvI,9520
+charset_normalizer/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+charset_normalizer/cli/__pycache__/__init__.cpython-39.pyc,,
+charset_normalizer/cli/__pycache__/normalizer.cpython-39.pyc,,
+charset_normalizer/cli/normalizer.py,sha256=H_2C2e81PqNCmUaXuwBJ5Lir_l0wwzELSxjPXcUL9O4,9453
+charset_normalizer/constant.py,sha256=rx8g-2vg-dIVV6iU6PWYWog9jBJQDvUr8vj4tcTo8OI,19011
+charset_normalizer/legacy.py,sha256=VXqlU-W1NdE7fUXV_DZ1BDUyyX4qnFegiedMI7i8n9A,3213
+charset_normalizer/md.py,sha256=OH-UeRKuprWUxoGffHNRH16W6Tmiu3OGYzzC8tZyarA,17654
+charset_normalizer/models.py,sha256=IfkHAPRbC7Veqic9C7XPtoEb4F08P4o970WIdBg7Elc,13332
+charset_normalizer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+charset_normalizer/utils.py,sha256=6vfdA59u0VQD3_dDXf2B8viuUWPo9xIfKq4b0nXX6Mo,9026
+charset_normalizer/version.py,sha256=GJ53Q4s3c6EeoKiY91GIgH_aP4WKIZ36Hg65AHVSUoQ,79
diff --git a/IKEA_scraper/.venv/Lib/site-packages/anyio-3.3.1.dist-info/WHEEL b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/WHEEL
similarity index 100%
rename from IKEA_scraper/.venv/Lib/site-packages/anyio-3.3.1.dist-info/WHEEL
rename to IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/WHEEL
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/entry_points.txt b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/entry_points.txt
similarity index 100%
rename from IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/entry_points.txt
rename to IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/entry_points.txt
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/top_level.txt b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/top_level.txt
similarity index 100%
rename from IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.4.dist-info/top_level.txt
rename to IKEA_scraper/.venv/Lib/site-packages/charset_normalizer-2.0.6.dist-info/top_level.txt
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__init__.py b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__init__.py
index f14a90b1..f899bce6 100644
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__init__.py
+++ b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__init__.py
@@ -8,24 +8,39 @@ All IANA character set names for which the Python core library provides codecs a
Basic usage:
>>> from charset_normalizer import from_bytes
- >>> results = from_bytes('Bсеки човек има право на образование. Oбразованието трябва да бъде безплатно, поне що се отнася до началното и основното образование.'.encode('utf_8'))
- >>> "utf_8" in results
- True
- >>> best_result = results.best()
- >>> str(best_result)
- 'Bсеки човек има право на образование. Oбразованието трябва да бъде безплатно, поне що се отнася до началното и основното образование.'
+ >>> results = from_bytes('Bсеки човек има право на образование. Oбразованието!'.encode('utf_8'))
+ >>> best_guess = results.best()
+ >>> str(best_guess)
+ 'Bсеки човек има право на образование. Oбразованието!'
Others methods and usages are available - see the full documentation
at .
:copyright: (c) 2021 by Ahmed TAHRI
:license: MIT, see LICENSE for more details.
"""
-from charset_normalizer.api import from_fp, from_path, from_bytes, normalize
-from charset_normalizer.legacy import detect
-from charset_normalizer.version import __version__, VERSION
-from charset_normalizer.models import CharsetMatch, CharsetMatches
+from .api import from_bytes, from_fp, from_path, normalize
+from .legacy import (
+ CharsetDetector,
+ CharsetDoctor,
+ CharsetNormalizerMatch,
+ CharsetNormalizerMatches,
+ detect,
+)
+from .models import CharsetMatch, CharsetMatches
+from .version import VERSION, __version__
-# Backward-compatible v1 imports
-from charset_normalizer.models import CharsetNormalizerMatch
-import charset_normalizer.api as CharsetDetector
-CharsetNormalizerMatches = CharsetDetector
+__all__ = (
+ "from_fp",
+ "from_path",
+ "from_bytes",
+ "normalize",
+ "detect",
+ "CharsetMatch",
+ "CharsetMatches",
+ "CharsetNormalizerMatch",
+ "CharsetNormalizerMatches",
+ "CharsetDetector",
+ "CharsetDoctor",
+ "__version__",
+ "VERSION",
+)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/__init__.cpython-39.pyc
index 7b2681ad..2de2a66d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/api.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/api.cpython-39.pyc
index 9b6d3c83..aa1e21d9 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/api.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/api.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/cd.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/cd.cpython-39.pyc
index 23f319a4..336083d0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/cd.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/cd.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/constant.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/constant.cpython-39.pyc
index 0b1f7ee4..633d0876 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/constant.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/constant.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/legacy.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/legacy.cpython-39.pyc
index 651b515e..e524e8f6 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/legacy.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/legacy.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/md.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/md.cpython-39.pyc
index 791b53b9..7649142f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/md.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/md.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/models.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/models.cpython-39.pyc
index 6ea64ae4..5a52b01f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/models.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/models.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/utils.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/utils.cpython-39.pyc
index 0aa430d5..af023b00 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/utils.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/utils.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/version.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/version.cpython-39.pyc
index 439ba4cb..b17a69ed 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/version.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/__pycache__/version.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/api.py b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/api.py
index fd023409..37795a4b 100644
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/api.py
+++ b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/api.py
@@ -1,38 +1,48 @@
-from os.path import splitext, basename
-from typing import List, BinaryIO, Optional, Set, Union
+from os.path import basename, splitext
+from typing import BinaryIO, List, Optional, Set
try:
from os import PathLike
-except ImportError:
- PathLike = Union[str, 'os.PathLike[str]'] # type: ignore
+except ImportError: # pragma: no cover
+ PathLike = str # type: ignore
-from charset_normalizer.constant import TOO_SMALL_SEQUENCE, TOO_BIG_SEQUENCE, IANA_SUPPORTED
-from charset_normalizer.md import mess_ratio
-from charset_normalizer.models import CharsetMatches, CharsetMatch
-from warnings import warn
import logging
-from charset_normalizer.utils import any_specified_encoding, is_multi_byte_encoding, identify_sig_or_bom, \
- should_strip_sig_or_bom, is_cp_similar, iana_name
-from charset_normalizer.cd import coherence_ratio, encoding_languages, mb_encoding_languages, merge_coherence_ratios
+from .cd import (
+ coherence_ratio,
+ encoding_languages,
+ mb_encoding_languages,
+ merge_coherence_ratios,
+)
+from .constant import IANA_SUPPORTED, TOO_BIG_SEQUENCE, TOO_SMALL_SEQUENCE
+from .md import mess_ratio
+from .models import CharsetMatch, CharsetMatches
+from .utils import (
+ any_specified_encoding,
+ iana_name,
+ identify_sig_or_bom,
+ is_cp_similar,
+ is_multi_byte_encoding,
+ should_strip_sig_or_bom,
+)
logger = logging.getLogger("charset_normalizer")
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
-handler.setFormatter(logging.Formatter('%(asctime)s | %(levelname)s | %(message)s'))
+handler.setFormatter(logging.Formatter("%(asctime)s | %(levelname)s | %(message)s"))
logger.addHandler(handler)
def from_bytes(
- sequences: bytes,
- steps: int = 5,
- chunk_size: int = 512,
- threshold: float = 0.2,
- cp_isolation: List[str] = None,
- cp_exclusion: List[str] = None,
- preemptive_behaviour: bool = True,
- explain: bool = False
+ sequences: bytes,
+ steps: int = 5,
+ chunk_size: int = 512,
+ threshold: float = 0.2,
+ cp_isolation: List[str] = None,
+ cp_exclusion: List[str] = None,
+ preemptive_behaviour: bool = True,
+ explain: bool = False,
) -> CharsetMatches:
"""
Given a raw bytes sequence, return the best possibles charset usable to render str objects.
@@ -49,6 +59,13 @@ def from_bytes(
This function will strip the SIG in the payload/sequence every time except on UTF-16, UTF-32.
"""
+ if not isinstance(sequences, (bytearray, bytes)):
+ raise TypeError(
+ "Expected object of type bytes or bytearray, got: {0}".format(
+ type(sequences)
+ )
+ )
+
if not explain:
logger.setLevel(logging.CRITICAL)
else:
@@ -57,41 +74,38 @@ def from_bytes(
length = len(sequences) # type: int
if length == 0:
- logger.warning("Given content is empty, stopping the process very early, returning empty utf_8 str match")
- return CharsetMatches(
- [
- CharsetMatch(
- sequences,
- "utf_8",
- 0.,
- False,
- [],
- ""
- )
- ]
+ logger.warning(
+ "Given content is empty, stopping the process very early, returning empty utf_8 str match"
)
+ return CharsetMatches([CharsetMatch(sequences, "utf_8", 0.0, False, [], "")])
if cp_isolation is not None:
- logger.warning('cp_isolation is set. use this flag for debugging purpose. '
- 'limited list of encoding allowed : %s.',
- ', '.join(cp_isolation))
+ logger.warning(
+ "cp_isolation is set. use this flag for debugging purpose. "
+ "limited list of encoding allowed : %s.",
+ ", ".join(cp_isolation),
+ )
cp_isolation = [iana_name(cp, False) for cp in cp_isolation]
else:
cp_isolation = []
if cp_exclusion is not None:
logger.warning(
- 'cp_exclusion is set. use this flag for debugging purpose. '
- 'limited list of encoding excluded : %s.',
- ', '.join(cp_exclusion))
+ "cp_exclusion is set. use this flag for debugging purpose. "
+ "limited list of encoding excluded : %s.",
+ ", ".join(cp_exclusion),
+ )
cp_exclusion = [iana_name(cp, False) for cp in cp_exclusion]
else:
cp_exclusion = []
if length <= (chunk_size * steps):
logger.warning(
- 'override steps (%i) and chunk_size (%i) as content does not fit (%i byte(s) given) parameters.',
- steps, chunk_size, length)
+ "override steps (%i) and chunk_size (%i) as content does not fit (%i byte(s) given) parameters.",
+ steps,
+ chunk_size,
+ length,
+ )
steps = 1
chunk_size = length
@@ -102,15 +116,30 @@ def from_bytes(
is_too_large_sequence = len(sequences) >= TOO_BIG_SEQUENCE # type: bool
if is_too_small_sequence:
- warn('Trying to detect encoding from a tiny portion of ({}) byte(s).'.format(length))
+ logger.warning(
+ "Trying to detect encoding from a tiny portion of ({}) byte(s).".format(
+ length
+ )
+ )
+ elif is_too_large_sequence:
+ logger.info(
+ "Using lazy str decoding because the payload is quite large, ({}) byte(s).".format(
+ length
+ )
+ )
prioritized_encodings = [] # type: List[str]
- specified_encoding = any_specified_encoding(sequences) if preemptive_behaviour is True else None # type: Optional[str]
+ specified_encoding = (
+ any_specified_encoding(sequences) if preemptive_behaviour is True else None
+ ) # type: Optional[str]
if specified_encoding is not None:
prioritized_encodings.append(specified_encoding)
- logger.info('Detected declarative mark in sequence. Priority +1 given for %s.', specified_encoding)
+ logger.info(
+ "Detected declarative mark in sequence. Priority +1 given for %s.",
+ specified_encoding,
+ )
tested = set() # type: Set[str]
tested_but_hard_failure = [] # type: List[str]
@@ -129,14 +158,18 @@ def from_bytes(
if sig_encoding is not None:
prioritized_encodings.append(sig_encoding)
- logger.info('Detected a SIG or BOM mark on first %i byte(s). Priority +1 given for %s.', len(sig_payload), sig_encoding)
+ logger.info(
+ "Detected a SIG or BOM mark on first %i byte(s). Priority +1 given for %s.",
+ len(sig_payload),
+ sig_encoding,
+ )
prioritized_encodings.append("ascii")
if "utf_8" not in prioritized_encodings:
prioritized_encodings.append("utf_8")
- for encoding_iana in prioritized_encodings+IANA_SUPPORTED:
+ for encoding_iana in prioritized_encodings + IANA_SUPPORTED:
if cp_isolation and encoding_iana not in cp_isolation:
continue
@@ -151,31 +184,46 @@ def from_bytes(
decoded_payload = None # type: Optional[str]
bom_or_sig_available = sig_encoding == encoding_iana # type: bool
- strip_sig_or_bom = bom_or_sig_available and should_strip_sig_or_bom(encoding_iana) # type: bool
+ strip_sig_or_bom = bom_or_sig_available and should_strip_sig_or_bom(
+ encoding_iana
+ ) # type: bool
if encoding_iana in {"utf_16", "utf_32"} and bom_or_sig_available is False:
- logger.info("Encoding %s wont be tested as-is because it require a BOM. Will try some sub-encoder LE/BE.", encoding_iana)
+ logger.info(
+ "Encoding %s wont be tested as-is because it require a BOM. Will try some sub-encoder LE/BE.",
+ encoding_iana,
+ )
continue
try:
is_multi_byte_decoder = is_multi_byte_encoding(encoding_iana) # type: bool
except (ModuleNotFoundError, ImportError):
- logger.debug("Encoding %s does not provide an IncrementalDecoder", encoding_iana)
+ logger.debug(
+ "Encoding %s does not provide an IncrementalDecoder", encoding_iana
+ )
continue
try:
if is_too_large_sequence and is_multi_byte_decoder is False:
str(
- sequences[:int(50e4)] if strip_sig_or_bom is False else sequences[len(sig_payload):int(50e4)],
- encoding=encoding_iana
+ sequences[: int(50e4)]
+ if strip_sig_or_bom is False
+ else sequences[len(sig_payload) : int(50e4)],
+ encoding=encoding_iana,
)
else:
decoded_payload = str(
- sequences if strip_sig_or_bom is False else sequences[len(sig_payload):],
- encoding=encoding_iana
+ sequences
+ if strip_sig_or_bom is False
+ else sequences[len(sig_payload) :],
+ encoding=encoding_iana,
)
except UnicodeDecodeError as e:
- logger.warning('Code page %s does not fit given bytes sequence at ALL. %s', encoding_iana, str(e))
+ logger.warning(
+ "Code page %s does not fit given bytes sequence at ALL. %s",
+ encoding_iana,
+ str(e),
+ )
tested_but_hard_failure.append(encoding_iana)
if not is_multi_byte_decoder:
single_byte_hard_failure_count += 1
@@ -194,19 +242,31 @@ def from_bytes(
break
if similar_soft_failure_test:
- logger.warning("%s is deemed too similar to code page %s and was consider unsuited already. Continuing!", encoding_iana, encoding_soft_failed)
+ logger.warning(
+ "%s is deemed too similar to code page %s and was consider unsuited already. Continuing!",
+ encoding_iana,
+ encoding_soft_failed,
+ )
continue
r_ = range(
0 if bom_or_sig_available is False else len(sig_payload),
length,
- int(length / steps)
+ int(length / steps),
)
- multi_byte_bonus = is_multi_byte_decoder and decoded_payload is not None and len(decoded_payload) < length # type: bool
+ multi_byte_bonus = (
+ is_multi_byte_decoder
+ and decoded_payload is not None
+ and len(decoded_payload) < length
+ ) # type: bool
if multi_byte_bonus:
- logger.info('Code page %s is a multi byte encoding table and it appear that at least one character was encoded using n-bytes.', encoding_iana)
+ logger.info(
+ "Code page %s is a multi byte encoding table and it appear that at least one character "
+ "was encoded using n-bytes.",
+ encoding_iana,
+ )
max_chunk_gave_up = int(len(r_) / 4) # type: int
@@ -219,51 +279,68 @@ def from_bytes(
md_ratios = []
for i in r_:
- cut_sequence = sequences[i:i + chunk_size]
+ cut_sequence = sequences[i : i + chunk_size]
if bom_or_sig_available and strip_sig_or_bom is False:
- cut_sequence = sig_payload+cut_sequence
+ cut_sequence = sig_payload + cut_sequence
chunk = cut_sequence.decode(encoding_iana, errors="ignore") # type: str
+ # multi-byte bad cutting detector and adjustment
+ # not the cleanest way to perform that fix but clever enough for now.
+ if is_multi_byte_decoder and i > 0 and sequences[i] >= 0x80:
+
+ chunk_partial_size_chk = (
+ 16 if chunk_size > 16 else chunk_size
+ ) # type: int
+
+ if (
+ decoded_payload
+ and chunk[:chunk_partial_size_chk] not in decoded_payload
+ ):
+ for j in range(i, i - 4, -1):
+ cut_sequence = sequences[j : i + chunk_size]
+
+ if bom_or_sig_available and strip_sig_or_bom is False:
+ cut_sequence = sig_payload + cut_sequence
+
+ chunk = cut_sequence.decode(encoding_iana, errors="ignore")
+
+ if chunk[:chunk_partial_size_chk] in decoded_payload:
+ break
+
md_chunks.append(chunk)
- md_ratios.append(
- mess_ratio(
- chunk,
- threshold
- )
- )
+ md_ratios.append(mess_ratio(chunk, threshold))
if md_ratios[-1] >= threshold:
early_stop_count += 1
- if (early_stop_count >= max_chunk_gave_up) or (bom_or_sig_available and strip_sig_or_bom is False):
+ if (early_stop_count >= max_chunk_gave_up) or (
+ bom_or_sig_available and strip_sig_or_bom is False
+ ):
break
if md_ratios:
mean_mess_ratio = sum(md_ratios) / len(md_ratios) # type: float
else:
- mean_mess_ratio = 0.
+ mean_mess_ratio = 0.0
if mean_mess_ratio >= threshold or early_stop_count >= max_chunk_gave_up:
tested_but_soft_failure.append(encoding_iana)
if not is_multi_byte_decoder:
single_byte_soft_failure_count += 1
- logger.warning('%s was excluded because of initial chaos probing. Gave up %i time(s). '
- 'Computed mean chaos is %f %%.',
- encoding_iana,
- early_stop_count,
- round(mean_mess_ratio * 100, ndigits=3))
+ logger.warning(
+ "%s was excluded because of initial chaos probing. Gave up %i time(s). "
+ "Computed mean chaos is %f %%.",
+ encoding_iana,
+ early_stop_count,
+ round(mean_mess_ratio * 100, ndigits=3),
+ )
# Preparing those fallbacks in case we got nothing.
if encoding_iana in ["ascii", "utf_8", specified_encoding]:
fallback_entry = CharsetMatch(
- sequences,
- encoding_iana,
- threshold,
- False,
- [],
- decoded_payload
+ sequences, encoding_iana, threshold, False, [], decoded_payload
)
if encoding_iana == specified_encoding:
fallback_specified = fallback_entry
@@ -274,9 +351,9 @@ def from_bytes(
continue
logger.info(
- '%s passed initial chaos probing. Mean measured chaos is %f %%',
+ "%s passed initial chaos probing. Mean measured chaos is %f %%",
encoding_iana,
- round(mean_mess_ratio * 100, ndigits=3)
+ round(mean_mess_ratio * 100, ndigits=3),
)
if not is_multi_byte_decoder:
@@ -285,21 +362,29 @@ def from_bytes(
target_languages = mb_encoding_languages(encoding_iana)
if target_languages:
- logger.info("{} should target any language(s) of {}".format(encoding_iana, str(target_languages)))
+ logger.info(
+ "{} should target any language(s) of {}".format(
+ encoding_iana, str(target_languages)
+ )
+ )
cd_ratios = []
for chunk in md_chunks:
- chunk_languages = coherence_ratio(chunk, 0.1, ",".join(target_languages) if target_languages else None)
-
- cd_ratios.append(
- chunk_languages
+ chunk_languages = coherence_ratio(
+ chunk, 0.1, ",".join(target_languages) if target_languages else None
)
+ cd_ratios.append(chunk_languages)
+
cd_ratios_merged = merge_coherence_ratios(cd_ratios)
if cd_ratios_merged:
- logger.info("We detected language {} using {}".format(cd_ratios_merged, encoding_iana))
+ logger.info(
+ "We detected language {} using {}".format(
+ cd_ratios_merged, encoding_iana
+ )
+ )
results.append(
CharsetMatch(
@@ -308,40 +393,53 @@ def from_bytes(
mean_mess_ratio,
bom_or_sig_available,
cd_ratios_merged,
- decoded_payload
+ decoded_payload,
)
)
- if encoding_iana in [specified_encoding, "ascii", "utf_8"] and mean_mess_ratio < 0.1:
- logger.info("%s is most likely the one. Stopping the process.", encoding_iana)
- return CharsetMatches(
- [results[encoding_iana]]
+ if (
+ encoding_iana in [specified_encoding, "ascii", "utf_8"]
+ and mean_mess_ratio < 0.1
+ ):
+ logger.info(
+ "%s is most likely the one. Stopping the process.", encoding_iana
)
+ return CharsetMatches([results[encoding_iana]])
if encoding_iana == sig_encoding:
logger.info(
"%s is most likely the one as we detected a BOM or SIG within the beginning of the sequence.",
- encoding_iana
- )
- return CharsetMatches(
- [results[encoding_iana]]
+ encoding_iana,
)
+ return CharsetMatches([results[encoding_iana]])
if results[-1].languages:
logger.info(
"Using %s code page we detected the following languages: %s",
encoding_iana,
- results[encoding_iana]._languages
+ results[encoding_iana]._languages,
)
if len(results) == 0:
if fallback_u8 or fallback_ascii or fallback_specified:
- logger.warning("Nothing got out of the detection process. Using ASCII/UTF-8/Specified fallback.")
+ logger.warning(
+ "Nothing got out of the detection process. Using ASCII/UTF-8/Specified fallback."
+ )
if fallback_specified:
- logger.warning("%s will be used as a fallback match", fallback_specified.encoding)
+ logger.warning(
+ "%s will be used as a fallback match", fallback_specified.encoding
+ )
results.append(fallback_specified)
- elif (fallback_u8 and fallback_ascii is None) or (fallback_u8 and fallback_u8.fingerprint != fallback_ascii.fingerprint):
+ elif (
+ (fallback_u8 and fallback_ascii is None)
+ or (
+ fallback_u8
+ and fallback_ascii
+ and fallback_u8.fingerprint != fallback_ascii.fingerprint
+ )
+ or (fallback_u8 is not None)
+ ):
logger.warning("utf_8 will be used as a fallback match")
results.append(fallback_u8)
elif fallback_ascii:
@@ -352,14 +450,14 @@ def from_bytes(
def from_fp(
- fp: BinaryIO,
- steps: int = 5,
- chunk_size: int = 512,
- threshold: float = 0.20,
- cp_isolation: List[str] = None,
- cp_exclusion: List[str] = None,
- preemptive_behaviour: bool = True,
- explain: bool = False
+ fp: BinaryIO,
+ steps: int = 5,
+ chunk_size: int = 512,
+ threshold: float = 0.20,
+ cp_isolation: List[str] = None,
+ cp_exclusion: List[str] = None,
+ preemptive_behaviour: bool = True,
+ explain: bool = False,
) -> CharsetMatches:
"""
Same thing than the function from_bytes but using a file pointer that is already ready.
@@ -373,29 +471,46 @@ def from_fp(
cp_isolation,
cp_exclusion,
preemptive_behaviour,
- explain
+ explain,
)
def from_path(
- path: PathLike,
- steps: int = 5,
- chunk_size: int = 512,
- threshold: float = 0.20,
- cp_isolation: List[str] = None,
- cp_exclusion: List[str] = None,
- preemptive_behaviour: bool = True,
- explain: bool = False
+ path: PathLike,
+ steps: int = 5,
+ chunk_size: int = 512,
+ threshold: float = 0.20,
+ cp_isolation: List[str] = None,
+ cp_exclusion: List[str] = None,
+ preemptive_behaviour: bool = True,
+ explain: bool = False,
) -> CharsetMatches:
"""
Same thing than the function from_bytes but with one extra step. Opening and reading given file path in binary mode.
Can raise IOError.
"""
- with open(path, 'rb') as fp:
- return from_fp(fp, steps, chunk_size, threshold, cp_isolation, cp_exclusion, preemptive_behaviour, explain)
+ with open(path, "rb") as fp:
+ return from_fp(
+ fp,
+ steps,
+ chunk_size,
+ threshold,
+ cp_isolation,
+ cp_exclusion,
+ preemptive_behaviour,
+ explain,
+ )
-def normalize(path: PathLike, steps: int = 5, chunk_size: int = 512, threshold: float = 0.20, cp_isolation: List[str] = None, cp_exclusion: List[str] = None, preemptive_behaviour: bool = True) -> CharsetMatch:
+def normalize(
+ path: PathLike,
+ steps: int = 5,
+ chunk_size: int = 512,
+ threshold: float = 0.20,
+ cp_isolation: List[str] = None,
+ cp_exclusion: List[str] = None,
+ preemptive_behaviour: bool = True,
+) -> CharsetMatch:
"""
Take a (text-based) file path and try to create another file next to it, this time using UTF-8.
"""
@@ -406,22 +521,26 @@ def normalize(path: PathLike, steps: int = 5, chunk_size: int = 512, threshold:
threshold,
cp_isolation,
cp_exclusion,
- preemptive_behaviour
+ preemptive_behaviour,
)
filename = basename(path)
target_extensions = list(splitext(filename))
if len(results) == 0:
- raise IOError('Unable to normalize "{}", no encoding charset seems to fit.'.format(filename))
+ raise IOError(
+ 'Unable to normalize "{}", no encoding charset seems to fit.'.format(
+ filename
+ )
+ )
result = results.best()
- target_extensions[0] += '-' + result.encoding # type: ignore
+ target_extensions[0] += "-" + result.encoding # type: ignore
- with open('{}'.format(path.replace(filename, ''.join(target_extensions))), 'wb') as fp:
- fp.write(
- result.output() # type: ignore
- )
+ with open(
+ "{}".format(str(path).replace(filename, "".join(target_extensions))), "wb"
+ ) as fp:
+ fp.write(result.output()) # type: ignore
return result # type: ignore
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/assets/__init__.py b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/assets/__init__.py
index 2d937736..830b2332 100644
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/assets/__init__.py
+++ b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/assets/__init__.py
@@ -7,46 +7,1213 @@ the core detection.
"""
from collections import OrderedDict
-
FREQUENCIES = OrderedDict(
[
- ('English', ['e', 'a', 't', 'i', 'o', 'n', 's', 'r', 'h', 'l', 'd', 'c', 'u', 'm', 'f', 'p', 'g', 'w', 'y', 'b', 'v', 'k', 'x', 'j', 'z', 'q']),
- ('German', ['e', 'n', 'i', 'r', 's', 't', 'a', 'd', 'h', 'u', 'l', 'g', 'o', 'c', 'm', 'b', 'f', 'k', 'w', 'z', 'p', 'v', 'ü', 'ä', 'ö', 'j']),
- ('French', ['e', 'a', 's', 'n', 'i', 't', 'r', 'l', 'u', 'o', 'd', 'c', 'p', 'm', 'é', 'v', 'g', 'f', 'b', 'h', 'q', 'à', 'x', 'è', 'y', 'j']),
- ('Dutch', ['e', 'n', 'a', 'i', 'r', 't', 'o', 'd', 's', 'l', 'g', 'h', 'v', 'm', 'u', 'k', 'c', 'p', 'b', 'w', 'j', 'z', 'f', 'y', 'x', 'ë']),
- ('Italian', ['e', 'i', 'a', 'o', 'n', 'l', 't', 'r', 's', 'c', 'd', 'u', 'p', 'm', 'g', 'v', 'f', 'b', 'z', 'h', 'q', 'è', 'à', 'k', 'y', 'ò']),
- ('Polish', ['a', 'i', 'o', 'e', 'n', 'r', 'z', 'w', 's', 'c', 't', 'k', 'y', 'd', 'p', 'm', 'u', 'l', 'j', 'ł', 'g', 'b', 'h', 'ą', 'ę', 'ó']),
- ('Spanish', ['e', 'a', 'o', 'n', 's', 'r', 'i', 'l', 'd', 't', 'c', 'u', 'm', 'p', 'b', 'g', 'v', 'f', 'y', 'ó', 'h', 'q', 'í', 'j', 'z', 'á']),
- ('Russian', ['о', 'а', 'е', 'и', 'н', 'с', 'т', 'р', 'в', 'л', 'к', 'м', 'д', 'п', 'у', 'г', 'я', 'ы', 'з', 'б', 'й', 'ь', 'ч', 'х', 'ж', 'ц']),
- ('Japanese', ['の', 'に', 'る', 'た', 'は', 'ー', 'と', 'し', 'を', 'で', 'て', 'が', 'い', 'ン', 'れ', 'な', '年', 'ス', 'っ', 'ル', 'か', 'ら', 'あ', 'さ', 'も', 'り']),
- ('Portuguese', ['a', 'e', 'o', 's', 'i', 'r', 'd', 'n', 't', 'm', 'u', 'c', 'l', 'p', 'g', 'v', 'b', 'f', 'h', 'ã', 'q', 'é', 'ç', 'á', 'z', 'í']),
- ('Swedish', ['e', 'a', 'n', 'r', 't', 's', 'i', 'l', 'd', 'o', 'm', 'k', 'g', 'v', 'h', 'f', 'u', 'p', 'ä', 'c', 'b', 'ö', 'å', 'y', 'j', 'x']),
- ('Chinese', ['的', '一', '是', '不', '了', '在', '人', '有', '我', '他', '这', '个', '们', '中', '来', '上', '大', '为', '和', '国', '地', '到', '以', '说', '时', '要', '就', '出', '会']),
- ('Ukrainian', ['о', 'а', 'н', 'і', 'и', 'р', 'в', 'т', 'е', 'с', 'к', 'л', 'у', 'д', 'м', 'п', 'з', 'я', 'ь', 'б', 'г', 'й', 'ч', 'х', 'ц', 'ї']),
- ('Norwegian', ['e', 'r', 'n', 't', 'a', 's', 'i', 'o', 'l', 'd', 'g', 'k', 'm', 'v', 'f', 'p', 'u', 'b', 'h', 'å', 'y', 'j', 'ø', 'c', 'æ', 'w']),
- ('Finnish', ['a', 'i', 'n', 't', 'e', 's', 'l', 'o', 'u', 'k', 'ä', 'm', 'r', 'v', 'j', 'h', 'p', 'y', 'd', 'ö', 'g', 'c', 'b', 'f', 'w', 'z']),
- ('Vietnamese', ['n', 'h', 't', 'i', 'c', 'g', 'a', 'o', 'u', 'm', 'l', 'r', 'à', 'đ', 's', 'e', 'v', 'p', 'b', 'y', 'ư', 'd', 'á', 'k', 'ộ', 'ế']),
- ('Czech', ['o', 'e', 'a', 'n', 't', 's', 'i', 'l', 'v', 'r', 'k', 'd', 'u', 'm', 'p', 'í', 'c', 'h', 'z', 'á', 'y', 'j', 'b', 'ě', 'é', 'ř']),
- ('Hungarian', ['e', 'a', 't', 'l', 's', 'n', 'k', 'r', 'i', 'o', 'z', 'á', 'é', 'g', 'm', 'b', 'y', 'v', 'd', 'h', 'u', 'p', 'j', 'ö', 'f', 'c']),
- ('Korean', ['이', '다', '에', '의', '는', '로', '하', '을', '가', '고', '지', '서', '한', '은', '기', '으', '년', '대', '사', '시', '를', '리', '도', '인', '스', '일']),
- ('Indonesian', ['a', 'n', 'e', 'i', 'r', 't', 'u', 's', 'd', 'k', 'm', 'l', 'g', 'p', 'b', 'o', 'h', 'y', 'j', 'c', 'w', 'f', 'v', 'z', 'x', 'q']),
- ('Turkish', ['a', 'e', 'i', 'n', 'r', 'l', 'ı', 'k', 'd', 't', 's', 'm', 'y', 'u', 'o', 'b', 'ü', 'ş', 'v', 'g', 'z', 'h', 'c', 'p', 'ç', 'ğ']),
- ('Romanian', ['e', 'i', 'a', 'r', 'n', 't', 'u', 'l', 'o', 'c', 's', 'd', 'p', 'm', 'ă', 'f', 'v', 'î', 'g', 'b', 'ș', 'ț', 'z', 'h', 'â', 'j']),
- ('Farsi', ['ا', 'ی', 'ر', 'د', 'ن', 'ه', 'و', 'م', 'ت', 'ب', 'س', 'ل', 'ک', 'ش', 'ز', 'ف', 'گ', 'ع', 'خ', 'ق', 'ج', 'آ', 'پ', 'ح', 'ط', 'ص']),
- ('Arabic', ['ا', 'ل', 'ي', 'م', 'و', 'ن', 'ر', 'ت', 'ب', 'ة', 'ع', 'د', 'س', 'ف', 'ه', 'ك', 'ق', 'أ', 'ح', 'ج', 'ش', 'ط', 'ص', 'ى', 'خ', 'إ']),
- ('Danish', ['e', 'r', 'n', 't', 'a', 'i', 's', 'd', 'l', 'o', 'g', 'm', 'k', 'f', 'v', 'u', 'b', 'h', 'p', 'å', 'y', 'ø', 'æ', 'c', 'j', 'w']),
- ('Serbian', ['а', 'и', 'о', 'е', 'н', 'р', 'с', 'у', 'т', 'к', 'ј', 'в', 'д', 'м', 'п', 'л', 'г', 'з', 'б', 'a', 'i', 'e', 'o', 'n', 'ц', 'ш']),
- ('Lithuanian', ['i', 'a', 's', 'o', 'r', 'e', 't', 'n', 'u', 'k', 'm', 'l', 'p', 'v', 'd', 'j', 'g', 'ė', 'b', 'y', 'ų', 'š', 'ž', 'c', 'ą', 'į']),
- ('Slovene', ['e', 'a', 'i', 'o', 'n', 'r', 's', 'l', 't', 'j', 'v', 'k', 'd', 'p', 'm', 'u', 'z', 'b', 'g', 'h', 'č', 'c', 'š', 'ž', 'f', 'y']),
- ('Slovak', ['o', 'a', 'e', 'n', 'i', 'r', 'v', 't', 's', 'l', 'k', 'd', 'm', 'p', 'u', 'c', 'h', 'j', 'b', 'z', 'á', 'y', 'ý', 'í', 'č', 'é']),
- ('Hebrew', ['י', 'ו', 'ה', 'ל', 'ר', 'ב', 'ת', 'מ', 'א', 'ש', 'נ', 'ע', 'ם', 'ד', 'ק', 'ח', 'פ', 'ס', 'כ', 'ג', 'ט', 'צ', 'ן', 'ז', 'ך']),
- ('Bulgarian', ['а', 'и', 'о', 'е', 'н', 'т', 'р', 'с', 'в', 'л', 'к', 'д', 'п', 'м', 'з', 'г', 'я', 'ъ', 'у', 'б', 'ч', 'ц', 'й', 'ж', 'щ', 'х']),
- ('Croatian', ['a', 'i', 'o', 'e', 'n', 'r', 'j', 's', 't', 'u', 'k', 'l', 'v', 'd', 'm', 'p', 'g', 'z', 'b', 'c', 'č', 'h', 'š', 'ž', 'ć', 'f']),
- ('Hindi', ['क', 'र', 'स', 'न', 'त', 'म', 'ह', 'प', 'य', 'ल', 'व', 'ज', 'द', 'ग', 'ब', 'श', 'ट', 'अ', 'ए', 'थ', 'भ', 'ड', 'च', 'ध', 'ष', 'इ']),
- ('Estonian', ['a', 'i', 'e', 's', 't', 'l', 'u', 'n', 'o', 'k', 'r', 'd', 'm', 'v', 'g', 'p', 'j', 'h', 'ä', 'b', 'õ', 'ü', 'f', 'c', 'ö', 'y']),
- ('Simple English', ['e', 'a', 't', 'i', 'o', 'n', 's', 'r', 'h', 'l', 'd', 'c', 'm', 'u', 'f', 'p', 'g', 'w', 'b', 'y', 'v', 'k', 'j', 'x', 'z', 'q']),
- ('Thai', ['า', 'น', 'ร', 'อ', 'ก', 'เ', 'ง', 'ม', 'ย', 'ล', 'ว', 'ด', 'ท', 'ส', 'ต', 'ะ', 'ป', 'บ', 'ค', 'ห', 'แ', 'จ', 'พ', 'ช', 'ข', 'ใ']),
- ('Greek', ['α', 'τ', 'ο', 'ι', 'ε', 'ν', 'ρ', 'σ', 'κ', 'η', 'π', 'ς', 'υ', 'μ', 'λ', 'ί', 'ό', 'ά', 'γ', 'έ', 'δ', 'ή', 'ω', 'χ', 'θ', 'ύ']),
- ('Tamil', ['க', 'த', 'ப', 'ட', 'ர', 'ம', 'ல', 'ன', 'வ', 'ற', 'ய', 'ள', 'ச', 'ந', 'இ', 'ண', 'அ', 'ஆ', 'ழ', 'ங', 'எ', 'உ', 'ஒ', 'ஸ']),
- ('Classical Chinese', ['之', '年', '為', '也', '以', '一', '人', '其', '者', '國', '有', '二', '十', '於', '曰', '三', '不', '大', '而', '子', '中', '五', '四'])]
+ (
+ "English",
+ [
+ "e",
+ "a",
+ "t",
+ "i",
+ "o",
+ "n",
+ "s",
+ "r",
+ "h",
+ "l",
+ "d",
+ "c",
+ "u",
+ "m",
+ "f",
+ "p",
+ "g",
+ "w",
+ "y",
+ "b",
+ "v",
+ "k",
+ "x",
+ "j",
+ "z",
+ "q",
+ ],
+ ),
+ (
+ "German",
+ [
+ "e",
+ "n",
+ "i",
+ "r",
+ "s",
+ "t",
+ "a",
+ "d",
+ "h",
+ "u",
+ "l",
+ "g",
+ "o",
+ "c",
+ "m",
+ "b",
+ "f",
+ "k",
+ "w",
+ "z",
+ "p",
+ "v",
+ "ü",
+ "ä",
+ "ö",
+ "j",
+ ],
+ ),
+ (
+ "French",
+ [
+ "e",
+ "a",
+ "s",
+ "n",
+ "i",
+ "t",
+ "r",
+ "l",
+ "u",
+ "o",
+ "d",
+ "c",
+ "p",
+ "m",
+ "é",
+ "v",
+ "g",
+ "f",
+ "b",
+ "h",
+ "q",
+ "à",
+ "x",
+ "è",
+ "y",
+ "j",
+ ],
+ ),
+ (
+ "Dutch",
+ [
+ "e",
+ "n",
+ "a",
+ "i",
+ "r",
+ "t",
+ "o",
+ "d",
+ "s",
+ "l",
+ "g",
+ "h",
+ "v",
+ "m",
+ "u",
+ "k",
+ "c",
+ "p",
+ "b",
+ "w",
+ "j",
+ "z",
+ "f",
+ "y",
+ "x",
+ "ë",
+ ],
+ ),
+ (
+ "Italian",
+ [
+ "e",
+ "i",
+ "a",
+ "o",
+ "n",
+ "l",
+ "t",
+ "r",
+ "s",
+ "c",
+ "d",
+ "u",
+ "p",
+ "m",
+ "g",
+ "v",
+ "f",
+ "b",
+ "z",
+ "h",
+ "q",
+ "è",
+ "à",
+ "k",
+ "y",
+ "ò",
+ ],
+ ),
+ (
+ "Polish",
+ [
+ "a",
+ "i",
+ "o",
+ "e",
+ "n",
+ "r",
+ "z",
+ "w",
+ "s",
+ "c",
+ "t",
+ "k",
+ "y",
+ "d",
+ "p",
+ "m",
+ "u",
+ "l",
+ "j",
+ "ł",
+ "g",
+ "b",
+ "h",
+ "ą",
+ "ę",
+ "ó",
+ ],
+ ),
+ (
+ "Spanish",
+ [
+ "e",
+ "a",
+ "o",
+ "n",
+ "s",
+ "r",
+ "i",
+ "l",
+ "d",
+ "t",
+ "c",
+ "u",
+ "m",
+ "p",
+ "b",
+ "g",
+ "v",
+ "f",
+ "y",
+ "ó",
+ "h",
+ "q",
+ "í",
+ "j",
+ "z",
+ "á",
+ ],
+ ),
+ (
+ "Russian",
+ [
+ "о",
+ "а",
+ "е",
+ "и",
+ "н",
+ "с",
+ "т",
+ "р",
+ "в",
+ "л",
+ "к",
+ "м",
+ "д",
+ "п",
+ "у",
+ "г",
+ "я",
+ "ы",
+ "з",
+ "б",
+ "й",
+ "ь",
+ "ч",
+ "х",
+ "ж",
+ "ц",
+ ],
+ ),
+ (
+ "Japanese",
+ [
+ "の",
+ "に",
+ "る",
+ "た",
+ "は",
+ "ー",
+ "と",
+ "し",
+ "を",
+ "で",
+ "て",
+ "が",
+ "い",
+ "ン",
+ "れ",
+ "な",
+ "年",
+ "ス",
+ "っ",
+ "ル",
+ "か",
+ "ら",
+ "あ",
+ "さ",
+ "も",
+ "り",
+ ],
+ ),
+ (
+ "Portuguese",
+ [
+ "a",
+ "e",
+ "o",
+ "s",
+ "i",
+ "r",
+ "d",
+ "n",
+ "t",
+ "m",
+ "u",
+ "c",
+ "l",
+ "p",
+ "g",
+ "v",
+ "b",
+ "f",
+ "h",
+ "ã",
+ "q",
+ "é",
+ "ç",
+ "á",
+ "z",
+ "í",
+ ],
+ ),
+ (
+ "Swedish",
+ [
+ "e",
+ "a",
+ "n",
+ "r",
+ "t",
+ "s",
+ "i",
+ "l",
+ "d",
+ "o",
+ "m",
+ "k",
+ "g",
+ "v",
+ "h",
+ "f",
+ "u",
+ "p",
+ "ä",
+ "c",
+ "b",
+ "ö",
+ "å",
+ "y",
+ "j",
+ "x",
+ ],
+ ),
+ (
+ "Chinese",
+ [
+ "的",
+ "一",
+ "是",
+ "不",
+ "了",
+ "在",
+ "人",
+ "有",
+ "我",
+ "他",
+ "这",
+ "个",
+ "们",
+ "中",
+ "来",
+ "上",
+ "大",
+ "为",
+ "和",
+ "国",
+ "地",
+ "到",
+ "以",
+ "说",
+ "时",
+ "要",
+ "就",
+ "出",
+ "会",
+ ],
+ ),
+ (
+ "Ukrainian",
+ [
+ "о",
+ "а",
+ "н",
+ "і",
+ "и",
+ "р",
+ "в",
+ "т",
+ "е",
+ "с",
+ "к",
+ "л",
+ "у",
+ "д",
+ "м",
+ "п",
+ "з",
+ "я",
+ "ь",
+ "б",
+ "г",
+ "й",
+ "ч",
+ "х",
+ "ц",
+ "ї",
+ ],
+ ),
+ (
+ "Norwegian",
+ [
+ "e",
+ "r",
+ "n",
+ "t",
+ "a",
+ "s",
+ "i",
+ "o",
+ "l",
+ "d",
+ "g",
+ "k",
+ "m",
+ "v",
+ "f",
+ "p",
+ "u",
+ "b",
+ "h",
+ "å",
+ "y",
+ "j",
+ "ø",
+ "c",
+ "æ",
+ "w",
+ ],
+ ),
+ (
+ "Finnish",
+ [
+ "a",
+ "i",
+ "n",
+ "t",
+ "e",
+ "s",
+ "l",
+ "o",
+ "u",
+ "k",
+ "ä",
+ "m",
+ "r",
+ "v",
+ "j",
+ "h",
+ "p",
+ "y",
+ "d",
+ "ö",
+ "g",
+ "c",
+ "b",
+ "f",
+ "w",
+ "z",
+ ],
+ ),
+ (
+ "Vietnamese",
+ [
+ "n",
+ "h",
+ "t",
+ "i",
+ "c",
+ "g",
+ "a",
+ "o",
+ "u",
+ "m",
+ "l",
+ "r",
+ "à",
+ "đ",
+ "s",
+ "e",
+ "v",
+ "p",
+ "b",
+ "y",
+ "ư",
+ "d",
+ "á",
+ "k",
+ "ộ",
+ "ế",
+ ],
+ ),
+ (
+ "Czech",
+ [
+ "o",
+ "e",
+ "a",
+ "n",
+ "t",
+ "s",
+ "i",
+ "l",
+ "v",
+ "r",
+ "k",
+ "d",
+ "u",
+ "m",
+ "p",
+ "í",
+ "c",
+ "h",
+ "z",
+ "á",
+ "y",
+ "j",
+ "b",
+ "ě",
+ "é",
+ "ř",
+ ],
+ ),
+ (
+ "Hungarian",
+ [
+ "e",
+ "a",
+ "t",
+ "l",
+ "s",
+ "n",
+ "k",
+ "r",
+ "i",
+ "o",
+ "z",
+ "á",
+ "é",
+ "g",
+ "m",
+ "b",
+ "y",
+ "v",
+ "d",
+ "h",
+ "u",
+ "p",
+ "j",
+ "ö",
+ "f",
+ "c",
+ ],
+ ),
+ (
+ "Korean",
+ [
+ "이",
+ "다",
+ "에",
+ "의",
+ "는",
+ "로",
+ "하",
+ "을",
+ "가",
+ "고",
+ "지",
+ "서",
+ "한",
+ "은",
+ "기",
+ "으",
+ "년",
+ "대",
+ "사",
+ "시",
+ "를",
+ "리",
+ "도",
+ "인",
+ "스",
+ "일",
+ ],
+ ),
+ (
+ "Indonesian",
+ [
+ "a",
+ "n",
+ "e",
+ "i",
+ "r",
+ "t",
+ "u",
+ "s",
+ "d",
+ "k",
+ "m",
+ "l",
+ "g",
+ "p",
+ "b",
+ "o",
+ "h",
+ "y",
+ "j",
+ "c",
+ "w",
+ "f",
+ "v",
+ "z",
+ "x",
+ "q",
+ ],
+ ),
+ (
+ "Turkish",
+ [
+ "a",
+ "e",
+ "i",
+ "n",
+ "r",
+ "l",
+ "ı",
+ "k",
+ "d",
+ "t",
+ "s",
+ "m",
+ "y",
+ "u",
+ "o",
+ "b",
+ "ü",
+ "ş",
+ "v",
+ "g",
+ "z",
+ "h",
+ "c",
+ "p",
+ "ç",
+ "ğ",
+ ],
+ ),
+ (
+ "Romanian",
+ [
+ "e",
+ "i",
+ "a",
+ "r",
+ "n",
+ "t",
+ "u",
+ "l",
+ "o",
+ "c",
+ "s",
+ "d",
+ "p",
+ "m",
+ "ă",
+ "f",
+ "v",
+ "î",
+ "g",
+ "b",
+ "ș",
+ "ț",
+ "z",
+ "h",
+ "â",
+ "j",
+ ],
+ ),
+ (
+ "Farsi",
+ [
+ "ا",
+ "ی",
+ "ر",
+ "د",
+ "ن",
+ "ه",
+ "و",
+ "م",
+ "ت",
+ "ب",
+ "س",
+ "ل",
+ "ک",
+ "ش",
+ "ز",
+ "ف",
+ "گ",
+ "ع",
+ "خ",
+ "ق",
+ "ج",
+ "آ",
+ "پ",
+ "ح",
+ "ط",
+ "ص",
+ ],
+ ),
+ (
+ "Arabic",
+ [
+ "ا",
+ "ل",
+ "ي",
+ "م",
+ "و",
+ "ن",
+ "ر",
+ "ت",
+ "ب",
+ "ة",
+ "ع",
+ "د",
+ "س",
+ "ف",
+ "ه",
+ "ك",
+ "ق",
+ "أ",
+ "ح",
+ "ج",
+ "ش",
+ "ط",
+ "ص",
+ "ى",
+ "خ",
+ "إ",
+ ],
+ ),
+ (
+ "Danish",
+ [
+ "e",
+ "r",
+ "n",
+ "t",
+ "a",
+ "i",
+ "s",
+ "d",
+ "l",
+ "o",
+ "g",
+ "m",
+ "k",
+ "f",
+ "v",
+ "u",
+ "b",
+ "h",
+ "p",
+ "å",
+ "y",
+ "ø",
+ "æ",
+ "c",
+ "j",
+ "w",
+ ],
+ ),
+ (
+ "Serbian",
+ [
+ "а",
+ "и",
+ "о",
+ "е",
+ "н",
+ "р",
+ "с",
+ "у",
+ "т",
+ "к",
+ "ј",
+ "в",
+ "д",
+ "м",
+ "п",
+ "л",
+ "г",
+ "з",
+ "б",
+ "a",
+ "i",
+ "e",
+ "o",
+ "n",
+ "ц",
+ "ш",
+ ],
+ ),
+ (
+ "Lithuanian",
+ [
+ "i",
+ "a",
+ "s",
+ "o",
+ "r",
+ "e",
+ "t",
+ "n",
+ "u",
+ "k",
+ "m",
+ "l",
+ "p",
+ "v",
+ "d",
+ "j",
+ "g",
+ "ė",
+ "b",
+ "y",
+ "ų",
+ "š",
+ "ž",
+ "c",
+ "ą",
+ "į",
+ ],
+ ),
+ (
+ "Slovene",
+ [
+ "e",
+ "a",
+ "i",
+ "o",
+ "n",
+ "r",
+ "s",
+ "l",
+ "t",
+ "j",
+ "v",
+ "k",
+ "d",
+ "p",
+ "m",
+ "u",
+ "z",
+ "b",
+ "g",
+ "h",
+ "č",
+ "c",
+ "š",
+ "ž",
+ "f",
+ "y",
+ ],
+ ),
+ (
+ "Slovak",
+ [
+ "o",
+ "a",
+ "e",
+ "n",
+ "i",
+ "r",
+ "v",
+ "t",
+ "s",
+ "l",
+ "k",
+ "d",
+ "m",
+ "p",
+ "u",
+ "c",
+ "h",
+ "j",
+ "b",
+ "z",
+ "á",
+ "y",
+ "ý",
+ "í",
+ "č",
+ "é",
+ ],
+ ),
+ (
+ "Hebrew",
+ [
+ "י",
+ "ו",
+ "ה",
+ "ל",
+ "ר",
+ "ב",
+ "ת",
+ "מ",
+ "א",
+ "ש",
+ "נ",
+ "ע",
+ "ם",
+ "ד",
+ "ק",
+ "ח",
+ "פ",
+ "ס",
+ "כ",
+ "ג",
+ "ט",
+ "צ",
+ "ן",
+ "ז",
+ "ך",
+ ],
+ ),
+ (
+ "Bulgarian",
+ [
+ "а",
+ "и",
+ "о",
+ "е",
+ "н",
+ "т",
+ "р",
+ "с",
+ "в",
+ "л",
+ "к",
+ "д",
+ "п",
+ "м",
+ "з",
+ "г",
+ "я",
+ "ъ",
+ "у",
+ "б",
+ "ч",
+ "ц",
+ "й",
+ "ж",
+ "щ",
+ "х",
+ ],
+ ),
+ (
+ "Croatian",
+ [
+ "a",
+ "i",
+ "o",
+ "e",
+ "n",
+ "r",
+ "j",
+ "s",
+ "t",
+ "u",
+ "k",
+ "l",
+ "v",
+ "d",
+ "m",
+ "p",
+ "g",
+ "z",
+ "b",
+ "c",
+ "č",
+ "h",
+ "š",
+ "ž",
+ "ć",
+ "f",
+ ],
+ ),
+ (
+ "Hindi",
+ [
+ "क",
+ "र",
+ "स",
+ "न",
+ "त",
+ "म",
+ "ह",
+ "प",
+ "य",
+ "ल",
+ "व",
+ "ज",
+ "द",
+ "ग",
+ "ब",
+ "श",
+ "ट",
+ "अ",
+ "ए",
+ "थ",
+ "भ",
+ "ड",
+ "च",
+ "ध",
+ "ष",
+ "इ",
+ ],
+ ),
+ (
+ "Estonian",
+ [
+ "a",
+ "i",
+ "e",
+ "s",
+ "t",
+ "l",
+ "u",
+ "n",
+ "o",
+ "k",
+ "r",
+ "d",
+ "m",
+ "v",
+ "g",
+ "p",
+ "j",
+ "h",
+ "ä",
+ "b",
+ "õ",
+ "ü",
+ "f",
+ "c",
+ "ö",
+ "y",
+ ],
+ ),
+ (
+ "Simple English",
+ [
+ "e",
+ "a",
+ "t",
+ "i",
+ "o",
+ "n",
+ "s",
+ "r",
+ "h",
+ "l",
+ "d",
+ "c",
+ "m",
+ "u",
+ "f",
+ "p",
+ "g",
+ "w",
+ "b",
+ "y",
+ "v",
+ "k",
+ "j",
+ "x",
+ "z",
+ "q",
+ ],
+ ),
+ (
+ "Thai",
+ [
+ "า",
+ "น",
+ "ร",
+ "อ",
+ "ก",
+ "เ",
+ "ง",
+ "ม",
+ "ย",
+ "ล",
+ "ว",
+ "ด",
+ "ท",
+ "ส",
+ "ต",
+ "ะ",
+ "ป",
+ "บ",
+ "ค",
+ "ห",
+ "แ",
+ "จ",
+ "พ",
+ "ช",
+ "ข",
+ "ใ",
+ ],
+ ),
+ (
+ "Greek",
+ [
+ "α",
+ "τ",
+ "ο",
+ "ι",
+ "ε",
+ "ν",
+ "ρ",
+ "σ",
+ "κ",
+ "η",
+ "π",
+ "ς",
+ "υ",
+ "μ",
+ "λ",
+ "ί",
+ "ό",
+ "ά",
+ "γ",
+ "έ",
+ "δ",
+ "ή",
+ "ω",
+ "χ",
+ "θ",
+ "ύ",
+ ],
+ ),
+ (
+ "Tamil",
+ [
+ "க",
+ "த",
+ "ப",
+ "ட",
+ "ர",
+ "ம",
+ "ல",
+ "ன",
+ "வ",
+ "ற",
+ "ய",
+ "ள",
+ "ச",
+ "ந",
+ "இ",
+ "ண",
+ "அ",
+ "ஆ",
+ "ழ",
+ "ங",
+ "எ",
+ "உ",
+ "ஒ",
+ "ஸ",
+ ],
+ ),
+ (
+ "Classical Chinese",
+ [
+ "之",
+ "年",
+ "為",
+ "也",
+ "以",
+ "一",
+ "人",
+ "其",
+ "者",
+ "國",
+ "有",
+ "二",
+ "十",
+ "於",
+ "曰",
+ "三",
+ "不",
+ "大",
+ "而",
+ "子",
+ "中",
+ "五",
+ "四",
+ ],
+ ),
+ ]
)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/assets/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/assets/__pycache__/__init__.cpython-39.pyc
index 4d2a6c70..72fa1491 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/assets/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/assets/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cd.py b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cd.py
index c4fc1ba5..78ecb2c6 100644
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cd.py
+++ b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cd.py
@@ -1,13 +1,13 @@
-from codecs import IncrementalDecoder
-from functools import lru_cache
-from typing import List, Set, Optional, Tuple, Dict
import importlib
-
-from charset_normalizer.models import CoherenceMatches
-from charset_normalizer.utils import unicode_range, is_unicode_range_secondary, is_multi_byte_encoding
-from charset_normalizer.md import is_suspiciously_successive_range
-from charset_normalizer.assets import FREQUENCIES
+from codecs import IncrementalDecoder
from collections import Counter
+from functools import lru_cache
+from typing import Dict, List, Optional, Set, Tuple
+
+from .assets import FREQUENCIES
+from .md import is_suspiciously_successive_range
+from .models import CoherenceMatches
+from .utils import is_multi_byte_encoding, is_unicode_range_secondary, unicode_range
def encoding_unicode_range(iana_name: str) -> List[str]:
@@ -17,15 +17,13 @@ def encoding_unicode_range(iana_name: str) -> List[str]:
if is_multi_byte_encoding(iana_name):
raise IOError("Function not supported on multi-byte code page")
- decoder = importlib.import_module('encodings.{}'.format(iana_name)).IncrementalDecoder # type: ignore
+ decoder = importlib.import_module("encodings.{}".format(iana_name)).IncrementalDecoder # type: ignore
p = decoder(errors="ignore") # type: IncrementalDecoder
seen_ranges = set() # type: Set[str]
for i in range(48, 255):
- chunk = p.decode(
- bytes([i])
- ) # type: str
+ chunk = p.decode(bytes([i])) # type: str
if chunk:
character_range = unicode_range(chunk) # type: Optional[str]
@@ -79,7 +77,12 @@ def mb_encoding_languages(iana_name: str) -> List[str]:
Multi-byte encoding language association. Some code page are heavily linked to particular language(s).
This function does the correspondence.
"""
- if iana_name.startswith("shift_") or iana_name.startswith("iso2022_jp") or iana_name.startswith("euc_j") or iana_name in {"cp932"}:
+ if (
+ iana_name.startswith("shift_")
+ or iana_name.startswith("iso2022_jp")
+ or iana_name.startswith("euc_j")
+ or iana_name in {"cp932"}
+ ):
return ["Japanese"]
if iana_name.startswith("gb") or iana_name in {"big5", "cp950", "big5hkscs"}:
return ["Chinese", "Classical Chinese"]
@@ -109,7 +112,9 @@ def alphabet_languages(characters: List[str]) -> List[str]:
return languages
-def characters_popularity_compare(language: str, ordered_characters: List[str]) -> float:
+def characters_popularity_compare(
+ language: str, ordered_characters: List[str]
+) -> float:
"""
Determine if a ordered characters list (by occurrence from most appearance to rarest) match a particular language.
The result is a ratio between 0. (absolutely no correspondence) and 1. (near perfect fit).
@@ -124,14 +129,30 @@ def characters_popularity_compare(language: str, ordered_characters: List[str])
if character not in FREQUENCIES[language]:
continue
- characters_before_source = FREQUENCIES[language][0:FREQUENCIES[language].index(character)] # type: List[str]
- characters_after_source = FREQUENCIES[language][FREQUENCIES[language].index(character):] # type: List[str]
+ characters_before_source = FREQUENCIES[language][
+ 0 : FREQUENCIES[language].index(character)
+ ] # type: List[str]
+ characters_after_source = FREQUENCIES[language][
+ FREQUENCIES[language].index(character) :
+ ] # type: List[str]
- characters_before = ordered_characters[0:ordered_characters.index(character)] # type: List[str]
- characters_after = ordered_characters[ordered_characters.index(character):] # type: List[str]
+ characters_before = ordered_characters[
+ 0 : ordered_characters.index(character)
+ ] # type: List[str]
+ characters_after = ordered_characters[
+ ordered_characters.index(character) :
+ ] # type: List[str]
- before_match_count = [e in characters_before for e in characters_before_source].count(True) # type: int
- after_match_count = [e in characters_after for e in characters_after_source].count(True) # type: int
+ before_match_count = [
+ e in characters_before for e in characters_before_source
+ ].count(
+ True
+ ) # type: int
+ after_match_count = [
+ e in characters_after for e in characters_after_source
+ ].count(
+ True
+ ) # type: int
if len(characters_before_source) == 0 and before_match_count <= 4:
character_approved_count += 1
@@ -141,7 +162,10 @@ def characters_popularity_compare(language: str, ordered_characters: List[str])
character_approved_count += 1
continue
- if before_match_count / len(characters_before_source) >= 0.4 or after_match_count / len(characters_after_source) >= 0.4:
+ if (
+ before_match_count / len(characters_before_source) >= 0.4
+ or after_match_count / len(characters_after_source) >= 0.4
+ ):
character_approved_count += 1
continue
@@ -160,12 +184,18 @@ def alpha_unicode_split(decoded_sequence: str) -> List[str]:
if character.isalpha() is False:
continue
- character_range = unicode_range(character) # type: str
+ character_range = unicode_range(character) # type: Optional[str]
+
+ if character_range is None:
+ continue
layer_target_range = None # type: Optional[str]
for discovered_range in layers:
- if is_suspiciously_successive_range(discovered_range, character_range) is False:
+ if (
+ is_suspiciously_successive_range(discovered_range, character_range)
+ is False
+ ):
layer_target_range = discovered_range
break
@@ -195,20 +225,17 @@ def merge_coherence_ratios(results: List[CoherenceMatches]) -> CoherenceMatches:
if language not in per_language_ratios:
per_language_ratios[language] = [ratio]
continue
- per_language_ratios[language].append(
- ratio
- )
+ per_language_ratios[language].append(ratio)
for language in per_language_ratios:
merge.append(
(
language,
round(
- sum(
- per_language_ratios[language]
- ) / len(per_language_ratios[language]),
- 4
- )
+ sum(per_language_ratios[language])
+ / len(per_language_ratios[language]),
+ 4,
+ ),
)
)
@@ -216,21 +243,24 @@ def merge_coherence_ratios(results: List[CoherenceMatches]) -> CoherenceMatches:
@lru_cache(maxsize=2048)
-def coherence_ratio(decoded_sequence: str, threshold: float = 0.1, lg_inclusion: Optional[str] = None) -> CoherenceMatches:
+def coherence_ratio(
+ decoded_sequence: str, threshold: float = 0.1, lg_inclusion: Optional[str] = None
+) -> CoherenceMatches:
"""
Detect ANY language that can be identified in given sequence. The sequence will be analysed by layers.
A layer = Character extraction by alphabets/ranges.
"""
results = [] # type: List[Tuple[str, float]]
+ lg_inclusion_list = [] # type: List[str]
sufficient_match_count = 0 # type: int
if lg_inclusion is not None:
- lg_inclusion = lg_inclusion.split(",")
+ lg_inclusion_list = lg_inclusion.split(",")
- if lg_inclusion is not None and "Latin Based" in lg_inclusion:
- lg_inclusion.remove("Latin Based")
+ if "Latin Based" in lg_inclusion_list:
+ lg_inclusion_list.remove("Latin Based")
for layer in alpha_unicode_split(decoded_sequence):
sequence_frequencies = Counter(layer) # type: Counter
@@ -243,17 +273,19 @@ def coherence_ratio(decoded_sequence: str, threshold: float = 0.1, lg_inclusion:
popular_character_ordered = [c for c, o in most_common] # type: List[str]
- for language in lg_inclusion or alphabet_languages(popular_character_ordered):
- ratio = characters_popularity_compare(language, popular_character_ordered) # type: float
+ for language in lg_inclusion_list or alphabet_languages(
+ popular_character_ordered
+ ):
+ ratio = characters_popularity_compare(
+ language, popular_character_ordered
+ ) # type: float
if ratio < threshold:
continue
elif ratio >= 0.8:
sufficient_match_count += 1
- results.append(
- (language, round(ratio, 4))
- )
+ results.append((language, round(ratio, 4)))
if sufficient_match_count >= 3:
break
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-39.pyc
index 2c99c476..d80cabac 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cli/__pycache__/normalizer.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cli/__pycache__/normalizer.cpython-39.pyc
index 5e9be9c5..87fec14c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cli/__pycache__/normalizer.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cli/__pycache__/normalizer.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cli/normalizer.py b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cli/normalizer.py
index c2ae18ea..f1911259 100644
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cli/normalizer.py
+++ b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/cli/normalizer.py
@@ -1,16 +1,16 @@
import argparse
import sys
-from os.path import abspath
from json import dumps
+from os.path import abspath
+from platform import python_version
+from typing import List
from charset_normalizer import from_fp
from charset_normalizer.models import CliDetectionResult
from charset_normalizer.version import __version__
-from platform import python_version
-
-def query_yes_no(question, default="yes"):
+def query_yes_no(question: str, default: str = "yes") -> bool:
"""Ask a yes/no question via input() and return their answer.
"question" is a string that is presented to the user.
@@ -22,8 +22,7 @@ def query_yes_no(question, default="yes"):
Credit goes to (c) https://stackoverflow.com/questions/3041986/apt-command-line-interface-like-yes-no-input
"""
- valid = {"yes": True, "y": True, "ye": True,
- "no": False, "n": False}
+ valid = {"yes": True, "y": True, "ye": True, "no": False, "n": False}
if default is None:
prompt = " [y/n] "
elif default == "yes":
@@ -36,16 +35,15 @@ def query_yes_no(question, default="yes"):
while True:
sys.stdout.write(question + prompt)
choice = input().lower()
- if default is not None and choice == '':
+ if default is not None and choice == "":
return valid[default]
elif choice in valid:
return valid[choice]
else:
- sys.stdout.write("Please respond with 'yes' or 'no' "
- "(or 'y' or 'n').\n")
+ sys.stdout.write("Please respond with 'yes' or 'no' " "(or 'y' or 'n').\n")
-def cli_detect(argv=None):
+def cli_detect(argv: List[str] = None) -> int:
"""
CLI assistant using ARGV and ArgumentParser
:param argv:
@@ -53,58 +51,112 @@ def cli_detect(argv=None):
"""
parser = argparse.ArgumentParser(
description="The Real First Universal Charset Detector. "
- "Discover originating encoding used on text file. "
- "Normalize text to unicode."
+ "Discover originating encoding used on text file. "
+ "Normalize text to unicode."
)
- parser.add_argument('files', type=argparse.FileType('rb'), nargs='+', help='File(s) to be analysed')
- parser.add_argument('-v', '--verbose', action="store_true", default=False, dest='verbose',
- help='Display complementary information about file if any. Stdout will contain logs about the detection process.')
- parser.add_argument('-a', '--with-alternative', action="store_true", default=False, dest='alternatives',
- help='Output complementary possibilities if any. Top-level JSON WILL be a list.')
- parser.add_argument('-n', '--normalize', action="store_true", default=False, dest='normalize',
- help='Permit to normalize input file. If not set, program does not write anything.')
- parser.add_argument('-m', '--minimal', action="store_true", default=False, dest='minimal',
- help='Only output the charset detected to STDOUT. Disabling JSON output.')
- parser.add_argument('-r', '--replace', action="store_true", default=False, dest='replace',
- help='Replace file when trying to normalize it instead of creating a new one.')
- parser.add_argument('-f', '--force', action="store_true", default=False, dest='force',
- help='Replace file without asking if you are sure, use this flag with caution.')
- parser.add_argument('-t', '--threshold', action="store", default=0.1, type=float, dest='threshold',
- help="Define a custom maximum amount of chaos allowed in decoded content. 0. <= chaos <= 1.")
+ parser.add_argument(
+ "files", type=argparse.FileType("rb"), nargs="+", help="File(s) to be analysed"
+ )
+ parser.add_argument(
+ "-v",
+ "--verbose",
+ action="store_true",
+ default=False,
+ dest="verbose",
+ help="Display complementary information about file if any. "
+ "Stdout will contain logs about the detection process.",
+ )
+ parser.add_argument(
+ "-a",
+ "--with-alternative",
+ action="store_true",
+ default=False,
+ dest="alternatives",
+ help="Output complementary possibilities if any. Top-level JSON WILL be a list.",
+ )
+ parser.add_argument(
+ "-n",
+ "--normalize",
+ action="store_true",
+ default=False,
+ dest="normalize",
+ help="Permit to normalize input file. If not set, program does not write anything.",
+ )
+ parser.add_argument(
+ "-m",
+ "--minimal",
+ action="store_true",
+ default=False,
+ dest="minimal",
+ help="Only output the charset detected to STDOUT. Disabling JSON output.",
+ )
+ parser.add_argument(
+ "-r",
+ "--replace",
+ action="store_true",
+ default=False,
+ dest="replace",
+ help="Replace file when trying to normalize it instead of creating a new one.",
+ )
+ parser.add_argument(
+ "-f",
+ "--force",
+ action="store_true",
+ default=False,
+ dest="force",
+ help="Replace file without asking if you are sure, use this flag with caution.",
+ )
+ parser.add_argument(
+ "-t",
+ "--threshold",
+ action="store",
+ default=0.1,
+ type=float,
+ dest="threshold",
+ help="Define a custom maximum amount of chaos allowed in decoded content. 0. <= chaos <= 1.",
+ )
parser.add_argument(
"--version",
action="version",
- version="Charset-Normalizer {} - Python {}".format(__version__, python_version()),
- help="Show version information and exit."
+ version="Charset-Normalizer {} - Python {}".format(
+ __version__, python_version()
+ ),
+ help="Show version information and exit.",
)
args = parser.parse_args(argv)
if args.replace is True and args.normalize is False:
- print('Use --replace in addition of --normalize only.', file=sys.stderr)
+ print("Use --replace in addition of --normalize only.", file=sys.stderr)
return 1
if args.force is True and args.replace is False:
- print('Use --force in addition of --replace only.', file=sys.stderr)
+ print("Use --force in addition of --replace only.", file=sys.stderr)
return 1
- if args.threshold < 0. or args.threshold > 1.:
- print('--threshold VALUE should be between 0. AND 1.', file=sys.stderr)
+ if args.threshold < 0.0 or args.threshold > 1.0:
+ print("--threshold VALUE should be between 0. AND 1.", file=sys.stderr)
return 1
x_ = []
for my_file in args.files:
- matches = from_fp(
- my_file,
- threshold=args.threshold,
- explain=args.verbose
- )
+ matches = from_fp(my_file, threshold=args.threshold, explain=args.verbose)
- if len(matches) == 0:
- print('Unable to identify originating encoding for "{}". {}'.format(my_file.name, 'Maybe try increasing maximum amount of chaos.' if args.threshold < 1. else ''), file=sys.stderr)
+ best_guess = matches.best()
+
+ if best_guess is None:
+ print(
+ 'Unable to identify originating encoding for "{}". {}'.format(
+ my_file.name,
+ "Maybe try increasing maximum amount of chaos."
+ if args.threshold < 1.0
+ else "",
+ ),
+ file=sys.stderr,
+ )
x_.append(
CliDetectionResult(
abspath(my_file.name),
@@ -114,80 +166,95 @@ def cli_detect(argv=None):
"Unknown",
[],
False,
- 1.,
- 0.,
+ 1.0,
+ 0.0,
None,
- True
+ True,
)
)
else:
-
- r_ = matches.best()
- p_ = r_.first()
-
x_.append(
CliDetectionResult(
abspath(my_file.name),
- p_.encoding,
- p_.encoding_aliases,
- [cp for cp in p_.could_be_from_charset if cp != p_.encoding],
- p_.language,
- p_.alphabets,
- p_.bom,
- p_.percent_chaos,
- p_.percent_coherence,
+ best_guess.encoding,
+ best_guess.encoding_aliases,
+ [
+ cp
+ for cp in best_guess.could_be_from_charset
+ if cp != best_guess.encoding
+ ],
+ best_guess.language,
+ best_guess.alphabets,
+ best_guess.bom,
+ best_guess.percent_chaos,
+ best_guess.percent_coherence,
None,
- True
+ True,
)
)
if len(matches) > 1 and args.alternatives:
for el in matches:
- if el != p_:
+ if el != best_guess:
x_.append(
CliDetectionResult(
abspath(my_file.name),
el.encoding,
el.encoding_aliases,
- [cp for cp in el.could_be_from_charset if cp != el.encoding],
+ [
+ cp
+ for cp in el.could_be_from_charset
+ if cp != el.encoding
+ ],
el.language,
el.alphabets,
el.bom,
el.percent_chaos,
el.percent_coherence,
None,
- False
+ False,
)
)
if args.normalize is True:
- if p_.encoding.startswith('utf') is True:
- print('"{}" file does not need to be normalized, as it already came from unicode.'.format(my_file.name), file=sys.stderr)
+ if best_guess.encoding.startswith("utf") is True:
+ print(
+ '"{}" file does not need to be normalized, as it already came from unicode.'.format(
+ my_file.name
+ ),
+ file=sys.stderr,
+ )
if my_file.closed is False:
my_file.close()
continue
- o_ = my_file.name.split('.') # type: list[str]
+ o_ = my_file.name.split(".") # type: List[str]
if args.replace is False:
- o_.insert(-1, p_.encoding)
+ o_.insert(-1, best_guess.encoding)
if my_file.closed is False:
my_file.close()
else:
- if args.force is False and query_yes_no(
- 'Are you sure to normalize "{}" by replacing it ?'.format(my_file.name), 'no') is False:
+ if (
+ args.force is False
+ and query_yes_no(
+ 'Are you sure to normalize "{}" by replacing it ?'.format(
+ my_file.name
+ ),
+ "no",
+ )
+ is False
+ ):
if my_file.closed is False:
my_file.close()
continue
try:
- x_[0].unicode_path = abspath('./{}'.format('.'.join(o_)))
+ x_[0].unicode_path = abspath("./{}".format(".".join(o_)))
- with open(x_[0].unicode_path, 'w', encoding='utf-8') as fp:
- fp.write(
- str(p_)
- )
+ with open(x_[0].unicode_path, "w", encoding="utf-8") as fp:
+ fp.write(str(best_guess))
except IOError as e:
print(str(e), file=sys.stderr)
if my_file.closed is False:
@@ -200,24 +267,25 @@ def cli_detect(argv=None):
if args.minimal is False:
print(
dumps(
- [
- el.__dict__ for el in x_
- ] if len(x_) > 1 else x_[0].__dict__,
+ [el.__dict__ for el in x_] if len(x_) > 1 else x_[0].__dict__,
ensure_ascii=True,
- indent=4
+ indent=4,
)
)
else:
- print(
- ', '.join(
- [
- el.encoding for el in x_
- ]
+ for my_file in args.files:
+ print(
+ ", ".join(
+ [
+ el.encoding if el.encoding else "undefined"
+ for el in x_
+ if el.path == abspath(my_file.name)
+ ]
+ )
)
- )
return 0
-if __name__ == '__main__':
+if __name__ == "__main__":
cli_detect()
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/constant.py b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/constant.py
index c9c96555..a0c20280 100644
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/constant.py
+++ b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/constant.py
@@ -1,64 +1,344 @@
from codecs import BOM_UTF8, BOM_UTF16_BE, BOM_UTF16_LE, BOM_UTF32_BE, BOM_UTF32_LE
-from typing import Dict, List, Union
-from encodings.aliases import aliases
-from re import compile as re_compile, IGNORECASE
from collections import OrderedDict
+from encodings.aliases import aliases
+from re import IGNORECASE, compile as re_compile
+from typing import Dict, List, Union
# Contain for each eligible encoding a list of/item bytes SIG/BOM
-ENCODING_MARKS = OrderedDict([
- ('utf_8', BOM_UTF8),
- ('utf_7', [
- b'\x2b\x2f\x76\x38',
- b'\x2b\x2f\x76\x39',
- b'\x2b\x2f\x76\x2b',
- b'\x2b\x2f\x76\x2f',
- b'\x2b\x2f\x76\x38\x2d'
- ]),
- ('gb18030', b'\x84\x31\x95\x33'),
- ('utf_32', [
- BOM_UTF32_BE,
- BOM_UTF32_LE
- ]),
- ('utf_16', [
- BOM_UTF16_BE,
- BOM_UTF16_LE
- ]),
-]) # type: Dict[str, Union[bytes, List[bytes]]]
+ENCODING_MARKS = OrderedDict(
+ [
+ ("utf_8", BOM_UTF8),
+ (
+ "utf_7",
+ [
+ b"\x2b\x2f\x76\x38",
+ b"\x2b\x2f\x76\x39",
+ b"\x2b\x2f\x76\x2b",
+ b"\x2b\x2f\x76\x2f",
+ b"\x2b\x2f\x76\x38\x2d",
+ ],
+ ),
+ ("gb18030", b"\x84\x31\x95\x33"),
+ ("utf_32", [BOM_UTF32_BE, BOM_UTF32_LE]),
+ ("utf_16", [BOM_UTF16_BE, BOM_UTF16_LE]),
+ ]
+) # type: Dict[str, Union[bytes, List[bytes]]]
TOO_SMALL_SEQUENCE = 32 # type: int
TOO_BIG_SEQUENCE = int(10e6) # type: int
UTF8_MAXIMAL_ALLOCATION = 1112064 # type: int
-UNICODE_RANGES_COMBINED = {'Control character': range(0, 31+1), 'Basic Latin': range(32, 127+1), 'Latin-1 Supplement': range(128, 255+1), 'Latin Extended-A': range(256, 383+1), 'Latin Extended-B': range(384, 591+1), 'IPA Extensions': range(592, 687+1), 'Spacing Modifier Letters': range(688, 767+1), 'Combining Diacritical Marks': range(768, 879+1), 'Greek and Coptic': range(880, 1023+1), 'Cyrillic': range(1024, 1279+1), 'Cyrillic Supplement': range(1280, 1327+1), 'Armenian': range(1328, 1423+1), 'Hebrew': range(1424, 1535+1), 'Arabic': range(1536, 1791+1), 'Syriac': range(1792, 1871+1), 'Arabic Supplement': range(1872, 1919+1), 'Thaana': range(1920, 1983+1), 'NKo': range(1984, 2047+1), 'Samaritan': range(2048, 2111+1), 'Mandaic': range(2112, 2143+1), 'Syriac Supplement': range(2144, 2159+1), 'Arabic Extended-A': range(2208, 2303+1), 'Devanagari': range(2304, 2431+1), 'Bengali': range(2432, 2559+1), 'Gurmukhi': range(2560, 2687+1), 'Gujarati': range(2688, 2815+1), 'Oriya': range(2816, 2943+1), 'Tamil': range(2944, 3071+1), 'Telugu': range(3072, 3199+1), 'Kannada': range(3200, 3327+1), 'Malayalam': range(3328, 3455+1), 'Sinhala': range(3456, 3583+1), 'Thai': range(3584, 3711+1), 'Lao': range(3712, 3839+1), 'Tibetan': range(3840, 4095+1), 'Myanmar': range(4096, 4255+1), 'Georgian': range(4256, 4351+1), 'Hangul Jamo': range(4352, 4607+1), 'Ethiopic': range(4608, 4991+1), 'Ethiopic Supplement': range(4992, 5023+1), 'Cherokee': range(5024, 5119+1), 'Unified Canadian Aboriginal Syllabics': range(5120, 5759+1), 'Ogham': range(5760, 5791+1), 'Runic': range(5792, 5887+1), 'Tagalog': range(5888, 5919+1), 'Hanunoo': range(5920, 5951+1), 'Buhid': range(5952, 5983+1), 'Tagbanwa': range(5984, 6015+1), 'Khmer': range(6016, 6143+1), 'Mongolian': range(6144, 6319+1), 'Unified Canadian Aboriginal Syllabics Extended': range(6320, 6399+1), 'Limbu': range(6400, 6479+1), 'Tai Le': range(6480, 6527+1), 'New Tai Lue': range(6528, 6623+1), 'Khmer Symbols': range(6624, 6655+1), 'Buginese': range(6656, 6687+1), 'Tai Tham': range(6688, 6831+1), 'Combining Diacritical Marks Extended': range(6832, 6911+1), 'Balinese': range(6912, 7039+1), 'Sundanese': range(7040, 7103+1), 'Batak': range(7104, 7167+1), 'Lepcha': range(7168, 7247+1), 'Ol Chiki': range(7248, 7295+1), 'Cyrillic Extended C': range(7296, 7311+1), 'Sundanese Supplement': range(7360, 7375+1), 'Vedic Extensions': range(7376, 7423+1), 'Phonetic Extensions': range(7424, 7551+1), 'Phonetic Extensions Supplement': range(7552, 7615+1), 'Combining Diacritical Marks Supplement': range(7616, 7679+1), 'Latin Extended Additional': range(7680, 7935+1), 'Greek Extended': range(7936, 8191+1), 'General Punctuation': range(8192, 8303+1), 'Superscripts and Subscripts': range(8304, 8351+1), 'Currency Symbols': range(8352, 8399+1), 'Combining Diacritical Marks for Symbols': range(8400, 8447+1), 'Letterlike Symbols': range(8448, 8527+1), 'Number Forms': range(8528, 8591+1), 'Arrows': range(8592, 8703+1), 'Mathematical Operators': range(8704, 8959+1), 'Miscellaneous Technical': range(8960, 9215+1), 'Control Pictures': range(9216, 9279+1), 'Optical Character Recognition': range(9280, 9311+1), 'Enclosed Alphanumerics': range(9312, 9471+1), 'Box Drawing': range(9472, 9599+1), 'Block Elements': range(9600, 9631+1), 'Geometric Shapes': range(9632, 9727+1), 'Miscellaneous Symbols': range(9728, 9983+1), 'Dingbats': range(9984, 10175+1), 'Miscellaneous Mathematical Symbols-A': range(10176, 10223+1), 'Supplemental Arrows-A': range(10224, 10239+1), 'Braille Patterns': range(10240, 10495+1), 'Supplemental Arrows-B': range(10496, 10623+1), 'Miscellaneous Mathematical Symbols-B': range(10624, 10751+1), 'Supplemental Mathematical Operators': range(10752, 11007+1), 'Miscellaneous Symbols and Arrows': range(11008, 11263+1), 'Glagolitic': range(11264, 11359+1), 'Latin Extended-C': range(11360, 11391+1), 'Coptic': range(11392, 11519+1), 'Georgian Supplement': range(11520, 11567+1), 'Tifinagh': range(11568, 11647+1), 'Ethiopic Extended': range(11648, 11743+1), 'Cyrillic Extended-A': range(11744, 11775+1), 'Supplemental Punctuation': range(11776, 11903+1), 'CJK Radicals Supplement': range(11904, 12031+1), 'Kangxi Radicals': range(12032, 12255+1), 'Ideographic Description Characters': range(12272, 12287+1), 'CJK Symbols and Punctuation': range(12288, 12351+1), 'Hiragana': range(12352, 12447+1), 'Katakana': range(12448, 12543+1), 'Bopomofo': range(12544, 12591+1), 'Hangul Compatibility Jamo': range(12592, 12687+1), 'Kanbun': range(12688, 12703+1), 'Bopomofo Extended': range(12704, 12735+1), 'CJK Strokes': range(12736, 12783+1), 'Katakana Phonetic Extensions': range(12784, 12799+1), 'Enclosed CJK Letters and Months': range(12800, 13055+1), 'CJK Compatibility': range(13056, 13311+1), 'CJK Unified Ideographs Extension A': range(13312, 19903+1), 'Yijing Hexagram Symbols': range(19904, 19967+1), 'CJK Unified Ideographs': range(19968, 40959+1), 'Yi Syllables': range(40960, 42127+1), 'Yi Radicals': range(42128, 42191+1), 'Lisu': range(42192, 42239+1), 'Vai': range(42240, 42559+1), 'Cyrillic Extended-B': range(42560, 42655+1), 'Bamum': range(42656, 42751+1), 'Modifier Tone Letters': range(42752, 42783+1), 'Latin Extended-D': range(42784, 43007+1), 'Syloti Nagri': range(43008, 43055+1), 'Common Indic Number Forms': range(43056, 43071+1), 'Phags-pa': range(43072, 43135+1), 'Saurashtra': range(43136, 43231+1), 'Devanagari Extended': range(43232, 43263+1), 'Kayah Li': range(43264, 43311+1), 'Rejang': range(43312, 43359+1), 'Hangul Jamo Extended-A': range(43360, 43391+1), 'Javanese': range(43392, 43487+1), 'Myanmar Extended-B': range(43488, 43519+1), 'Cham': range(43520, 43615+1), 'Myanmar Extended-A': range(43616, 43647+1), 'Tai Viet': range(43648, 43743+1), 'Meetei Mayek Extensions': range(43744, 43775+1), 'Ethiopic Extended-A': range(43776, 43823+1), 'Latin Extended-E': range(43824, 43887+1), 'Cherokee Supplement': range(43888, 43967+1), 'Meetei Mayek': range(43968, 44031+1), 'Hangul Syllables': range(44032, 55215+1), 'Hangul Jamo Extended-B': range(55216, 55295+1), 'High Surrogates': range(55296, 56191+1), 'High Private Use Surrogates': range(56192, 56319+1), 'Low Surrogates': range(56320, 57343+1), 'Private Use Area': range(57344, 63743+1), 'CJK Compatibility Ideographs': range(63744, 64255+1), 'Alphabetic Presentation Forms': range(64256, 64335+1), 'Arabic Presentation Forms-A': range(64336, 65023+1), 'Variation Selectors': range(65024, 65039+1), 'Vertical Forms': range(65040, 65055+1), 'Combining Half Marks': range(65056, 65071+1), 'CJK Compatibility Forms': range(65072, 65103+1), 'Small Form Variants': range(65104, 65135+1), 'Arabic Presentation Forms-B': range(65136, 65279+1), 'Halfwidth and Fullwidth Forms': range(65280, 65519+1), 'Specials': range(65520, 65535+1), 'Linear B Syllabary': range(65536, 65663+1), 'Linear B Ideograms': range(65664, 65791+1), 'Aegean Numbers': range(65792, 65855+1), 'Ancient Greek Numbers': range(65856, 65935+1), 'Ancient Symbols': range(65936, 65999+1), 'Phaistos Disc': range(66000, 66047+1), 'Lycian': range(66176, 66207+1), 'Carian': range(66208, 66271+1), 'Coptic Epact Numbers': range(66272, 66303+1), 'Old Italic': range(66304, 66351+1), 'Gothic': range(66352, 66383+1), 'Old Permic': range(66384, 66431+1), 'Ugaritic': range(66432, 66463+1), 'Old Persian': range(66464, 66527+1), 'Deseret': range(66560, 66639+1), 'Shavian': range(66640, 66687+1), 'Osmanya': range(66688, 66735+1), 'Osage': range(66736, 66815+1), 'Elbasan': range(66816, 66863+1), 'Caucasian Albanian': range(66864, 66927+1), 'Linear A': range(67072, 67455+1), 'Cypriot Syllabary': range(67584, 67647+1), 'Imperial Aramaic': range(67648, 67679+1), 'Palmyrene': range(67680, 67711+1), 'Nabataean': range(67712, 67759+1), 'Hatran': range(67808, 67839+1), 'Phoenician': range(67840, 67871+1), 'Lydian': range(67872, 67903+1), 'Meroitic Hieroglyphs': range(67968, 67999+1), 'Meroitic Cursive': range(68000, 68095+1), 'Kharoshthi': range(68096, 68191+1), 'Old South Arabian': range(68192, 68223+1), 'Old North Arabian': range(68224, 68255+1), 'Manichaean': range(68288, 68351+1), 'Avestan': range(68352, 68415+1), 'Inscriptional Parthian': range(68416, 68447+1), 'Inscriptional Pahlavi': range(68448, 68479+1), 'Psalter Pahlavi': range(68480, 68527+1), 'Old Turkic': range(68608, 68687+1), 'Old Hungarian': range(68736, 68863+1), 'Rumi Numeral Symbols': range(69216, 69247+1), 'Brahmi': range(69632, 69759+1), 'Kaithi': range(69760, 69839+1), 'Sora Sompeng': range(69840, 69887+1), 'Chakma': range(69888, 69967+1), 'Mahajani': range(69968, 70015+1), 'Sharada': range(70016, 70111+1), 'Sinhala Archaic Numbers': range(70112, 70143+1), 'Khojki': range(70144, 70223+1), 'Multani': range(70272, 70319+1), 'Khudawadi': range(70320, 70399+1), 'Grantha': range(70400, 70527+1), 'Newa': range(70656, 70783+1), 'Tirhuta': range(70784, 70879+1), 'Siddham': range(71040, 71167+1), 'Modi': range(71168, 71263+1), 'Mongolian Supplement': range(71264, 71295+1), 'Takri': range(71296, 71375+1), 'Ahom': range(71424, 71487+1), 'Warang Citi': range(71840, 71935+1), 'Zanabazar Square': range(72192, 72271+1), 'Soyombo': range(72272, 72367+1), 'Pau Cin Hau': range(72384, 72447+1), 'Bhaiksuki': range(72704, 72815+1), 'Marchen': range(72816, 72895+1), 'Masaram Gondi': range(72960, 73055+1), 'Cuneiform': range(73728, 74751+1), 'Cuneiform Numbers and Punctuation': range(74752, 74879+1), 'Early Dynastic Cuneiform': range(74880, 75087+1), 'Egyptian Hieroglyphs': range(77824, 78895+1), 'Anatolian Hieroglyphs': range(82944, 83583+1), 'Bamum Supplement': range(92160, 92735+1), 'Mro': range(92736, 92783+1), 'Bassa Vah': range(92880, 92927+1), 'Pahawh Hmong': range(92928, 93071+1), 'Miao': range(93952, 94111+1), 'Ideographic Symbols and Punctuation': range(94176, 94207+1), 'Tangut': range(94208, 100351+1), 'Tangut Components': range(100352, 101119+1), 'Kana Supplement': range(110592, 110847+1), 'Kana Extended-A': range(110848, 110895+1), 'Nushu': range(110960, 111359+1), 'Duployan': range(113664, 113823+1), 'Shorthand Format Controls': range(113824, 113839+1), 'Byzantine Musical Symbols': range(118784, 119039+1), 'Musical Symbols': range(119040, 119295+1), 'Ancient Greek Musical Notation': range(119296, 119375+1), 'Tai Xuan Jing Symbols': range(119552, 119647+1), 'Counting Rod Numerals': range(119648, 119679+1), 'Mathematical Alphanumeric Symbols': range(119808, 120831+1), 'Sutton SignWriting': range(120832, 121519+1), 'Glagolitic Supplement': range(122880, 122927+1), 'Mende Kikakui': range(124928, 125151+1), 'Adlam': range(125184, 125279+1), 'Arabic Mathematical Alphabetic Symbols': range(126464, 126719+1), 'Mahjong Tiles': range(126976, 127023+1), 'Domino Tiles': range(127024, 127135+1), 'Playing Cards': range(127136, 127231+1), 'Enclosed Alphanumeric Supplement': range(127232, 127487+1), 'Enclosed Ideographic Supplement': range(127488, 127743+1), 'Miscellaneous Symbols and Pictographs': range(127744, 128511+1), 'Emoticons range(Emoji)': range(128512, 128591+1), 'Ornamental Dingbats': range(128592, 128639+1), 'Transport and Map Symbols': range(128640, 128767+1), 'Alchemical Symbols': range(128768, 128895+1), 'Geometric Shapes Extended': range(128896, 129023+1), 'Supplemental Arrows-C': range(129024, 129279+1), 'Supplemental Symbols and Pictographs': range(129280, 129535+1), 'CJK Unified Ideographs Extension B': range(131072, 173791+1), 'CJK Unified Ideographs Extension C': range(173824, 177983+1), 'CJK Unified Ideographs Extension D': range(177984, 178207+1), 'CJK Unified Ideographs Extension E': range(178208, 183983+1), 'CJK Unified Ideographs Extension F': range(183984, 191471+1), 'CJK Compatibility Ideographs Supplement': range(194560, 195103+1), 'Tags': range(917504, 917631+1), 'Variation Selectors Supplement': range(917760, 917999+1)} # type: Dict[str, range]
+UNICODE_RANGES_COMBINED = {
+ "Control character": range(0, 31 + 1),
+ "Basic Latin": range(32, 127 + 1),
+ "Latin-1 Supplement": range(128, 255 + 1),
+ "Latin Extended-A": range(256, 383 + 1),
+ "Latin Extended-B": range(384, 591 + 1),
+ "IPA Extensions": range(592, 687 + 1),
+ "Spacing Modifier Letters": range(688, 767 + 1),
+ "Combining Diacritical Marks": range(768, 879 + 1),
+ "Greek and Coptic": range(880, 1023 + 1),
+ "Cyrillic": range(1024, 1279 + 1),
+ "Cyrillic Supplement": range(1280, 1327 + 1),
+ "Armenian": range(1328, 1423 + 1),
+ "Hebrew": range(1424, 1535 + 1),
+ "Arabic": range(1536, 1791 + 1),
+ "Syriac": range(1792, 1871 + 1),
+ "Arabic Supplement": range(1872, 1919 + 1),
+ "Thaana": range(1920, 1983 + 1),
+ "NKo": range(1984, 2047 + 1),
+ "Samaritan": range(2048, 2111 + 1),
+ "Mandaic": range(2112, 2143 + 1),
+ "Syriac Supplement": range(2144, 2159 + 1),
+ "Arabic Extended-A": range(2208, 2303 + 1),
+ "Devanagari": range(2304, 2431 + 1),
+ "Bengali": range(2432, 2559 + 1),
+ "Gurmukhi": range(2560, 2687 + 1),
+ "Gujarati": range(2688, 2815 + 1),
+ "Oriya": range(2816, 2943 + 1),
+ "Tamil": range(2944, 3071 + 1),
+ "Telugu": range(3072, 3199 + 1),
+ "Kannada": range(3200, 3327 + 1),
+ "Malayalam": range(3328, 3455 + 1),
+ "Sinhala": range(3456, 3583 + 1),
+ "Thai": range(3584, 3711 + 1),
+ "Lao": range(3712, 3839 + 1),
+ "Tibetan": range(3840, 4095 + 1),
+ "Myanmar": range(4096, 4255 + 1),
+ "Georgian": range(4256, 4351 + 1),
+ "Hangul Jamo": range(4352, 4607 + 1),
+ "Ethiopic": range(4608, 4991 + 1),
+ "Ethiopic Supplement": range(4992, 5023 + 1),
+ "Cherokee": range(5024, 5119 + 1),
+ "Unified Canadian Aboriginal Syllabics": range(5120, 5759 + 1),
+ "Ogham": range(5760, 5791 + 1),
+ "Runic": range(5792, 5887 + 1),
+ "Tagalog": range(5888, 5919 + 1),
+ "Hanunoo": range(5920, 5951 + 1),
+ "Buhid": range(5952, 5983 + 1),
+ "Tagbanwa": range(5984, 6015 + 1),
+ "Khmer": range(6016, 6143 + 1),
+ "Mongolian": range(6144, 6319 + 1),
+ "Unified Canadian Aboriginal Syllabics Extended": range(6320, 6399 + 1),
+ "Limbu": range(6400, 6479 + 1),
+ "Tai Le": range(6480, 6527 + 1),
+ "New Tai Lue": range(6528, 6623 + 1),
+ "Khmer Symbols": range(6624, 6655 + 1),
+ "Buginese": range(6656, 6687 + 1),
+ "Tai Tham": range(6688, 6831 + 1),
+ "Combining Diacritical Marks Extended": range(6832, 6911 + 1),
+ "Balinese": range(6912, 7039 + 1),
+ "Sundanese": range(7040, 7103 + 1),
+ "Batak": range(7104, 7167 + 1),
+ "Lepcha": range(7168, 7247 + 1),
+ "Ol Chiki": range(7248, 7295 + 1),
+ "Cyrillic Extended C": range(7296, 7311 + 1),
+ "Sundanese Supplement": range(7360, 7375 + 1),
+ "Vedic Extensions": range(7376, 7423 + 1),
+ "Phonetic Extensions": range(7424, 7551 + 1),
+ "Phonetic Extensions Supplement": range(7552, 7615 + 1),
+ "Combining Diacritical Marks Supplement": range(7616, 7679 + 1),
+ "Latin Extended Additional": range(7680, 7935 + 1),
+ "Greek Extended": range(7936, 8191 + 1),
+ "General Punctuation": range(8192, 8303 + 1),
+ "Superscripts and Subscripts": range(8304, 8351 + 1),
+ "Currency Symbols": range(8352, 8399 + 1),
+ "Combining Diacritical Marks for Symbols": range(8400, 8447 + 1),
+ "Letterlike Symbols": range(8448, 8527 + 1),
+ "Number Forms": range(8528, 8591 + 1),
+ "Arrows": range(8592, 8703 + 1),
+ "Mathematical Operators": range(8704, 8959 + 1),
+ "Miscellaneous Technical": range(8960, 9215 + 1),
+ "Control Pictures": range(9216, 9279 + 1),
+ "Optical Character Recognition": range(9280, 9311 + 1),
+ "Enclosed Alphanumerics": range(9312, 9471 + 1),
+ "Box Drawing": range(9472, 9599 + 1),
+ "Block Elements": range(9600, 9631 + 1),
+ "Geometric Shapes": range(9632, 9727 + 1),
+ "Miscellaneous Symbols": range(9728, 9983 + 1),
+ "Dingbats": range(9984, 10175 + 1),
+ "Miscellaneous Mathematical Symbols-A": range(10176, 10223 + 1),
+ "Supplemental Arrows-A": range(10224, 10239 + 1),
+ "Braille Patterns": range(10240, 10495 + 1),
+ "Supplemental Arrows-B": range(10496, 10623 + 1),
+ "Miscellaneous Mathematical Symbols-B": range(10624, 10751 + 1),
+ "Supplemental Mathematical Operators": range(10752, 11007 + 1),
+ "Miscellaneous Symbols and Arrows": range(11008, 11263 + 1),
+ "Glagolitic": range(11264, 11359 + 1),
+ "Latin Extended-C": range(11360, 11391 + 1),
+ "Coptic": range(11392, 11519 + 1),
+ "Georgian Supplement": range(11520, 11567 + 1),
+ "Tifinagh": range(11568, 11647 + 1),
+ "Ethiopic Extended": range(11648, 11743 + 1),
+ "Cyrillic Extended-A": range(11744, 11775 + 1),
+ "Supplemental Punctuation": range(11776, 11903 + 1),
+ "CJK Radicals Supplement": range(11904, 12031 + 1),
+ "Kangxi Radicals": range(12032, 12255 + 1),
+ "Ideographic Description Characters": range(12272, 12287 + 1),
+ "CJK Symbols and Punctuation": range(12288, 12351 + 1),
+ "Hiragana": range(12352, 12447 + 1),
+ "Katakana": range(12448, 12543 + 1),
+ "Bopomofo": range(12544, 12591 + 1),
+ "Hangul Compatibility Jamo": range(12592, 12687 + 1),
+ "Kanbun": range(12688, 12703 + 1),
+ "Bopomofo Extended": range(12704, 12735 + 1),
+ "CJK Strokes": range(12736, 12783 + 1),
+ "Katakana Phonetic Extensions": range(12784, 12799 + 1),
+ "Enclosed CJK Letters and Months": range(12800, 13055 + 1),
+ "CJK Compatibility": range(13056, 13311 + 1),
+ "CJK Unified Ideographs Extension A": range(13312, 19903 + 1),
+ "Yijing Hexagram Symbols": range(19904, 19967 + 1),
+ "CJK Unified Ideographs": range(19968, 40959 + 1),
+ "Yi Syllables": range(40960, 42127 + 1),
+ "Yi Radicals": range(42128, 42191 + 1),
+ "Lisu": range(42192, 42239 + 1),
+ "Vai": range(42240, 42559 + 1),
+ "Cyrillic Extended-B": range(42560, 42655 + 1),
+ "Bamum": range(42656, 42751 + 1),
+ "Modifier Tone Letters": range(42752, 42783 + 1),
+ "Latin Extended-D": range(42784, 43007 + 1),
+ "Syloti Nagri": range(43008, 43055 + 1),
+ "Common Indic Number Forms": range(43056, 43071 + 1),
+ "Phags-pa": range(43072, 43135 + 1),
+ "Saurashtra": range(43136, 43231 + 1),
+ "Devanagari Extended": range(43232, 43263 + 1),
+ "Kayah Li": range(43264, 43311 + 1),
+ "Rejang": range(43312, 43359 + 1),
+ "Hangul Jamo Extended-A": range(43360, 43391 + 1),
+ "Javanese": range(43392, 43487 + 1),
+ "Myanmar Extended-B": range(43488, 43519 + 1),
+ "Cham": range(43520, 43615 + 1),
+ "Myanmar Extended-A": range(43616, 43647 + 1),
+ "Tai Viet": range(43648, 43743 + 1),
+ "Meetei Mayek Extensions": range(43744, 43775 + 1),
+ "Ethiopic Extended-A": range(43776, 43823 + 1),
+ "Latin Extended-E": range(43824, 43887 + 1),
+ "Cherokee Supplement": range(43888, 43967 + 1),
+ "Meetei Mayek": range(43968, 44031 + 1),
+ "Hangul Syllables": range(44032, 55215 + 1),
+ "Hangul Jamo Extended-B": range(55216, 55295 + 1),
+ "High Surrogates": range(55296, 56191 + 1),
+ "High Private Use Surrogates": range(56192, 56319 + 1),
+ "Low Surrogates": range(56320, 57343 + 1),
+ "Private Use Area": range(57344, 63743 + 1),
+ "CJK Compatibility Ideographs": range(63744, 64255 + 1),
+ "Alphabetic Presentation Forms": range(64256, 64335 + 1),
+ "Arabic Presentation Forms-A": range(64336, 65023 + 1),
+ "Variation Selectors": range(65024, 65039 + 1),
+ "Vertical Forms": range(65040, 65055 + 1),
+ "Combining Half Marks": range(65056, 65071 + 1),
+ "CJK Compatibility Forms": range(65072, 65103 + 1),
+ "Small Form Variants": range(65104, 65135 + 1),
+ "Arabic Presentation Forms-B": range(65136, 65279 + 1),
+ "Halfwidth and Fullwidth Forms": range(65280, 65519 + 1),
+ "Specials": range(65520, 65535 + 1),
+ "Linear B Syllabary": range(65536, 65663 + 1),
+ "Linear B Ideograms": range(65664, 65791 + 1),
+ "Aegean Numbers": range(65792, 65855 + 1),
+ "Ancient Greek Numbers": range(65856, 65935 + 1),
+ "Ancient Symbols": range(65936, 65999 + 1),
+ "Phaistos Disc": range(66000, 66047 + 1),
+ "Lycian": range(66176, 66207 + 1),
+ "Carian": range(66208, 66271 + 1),
+ "Coptic Epact Numbers": range(66272, 66303 + 1),
+ "Old Italic": range(66304, 66351 + 1),
+ "Gothic": range(66352, 66383 + 1),
+ "Old Permic": range(66384, 66431 + 1),
+ "Ugaritic": range(66432, 66463 + 1),
+ "Old Persian": range(66464, 66527 + 1),
+ "Deseret": range(66560, 66639 + 1),
+ "Shavian": range(66640, 66687 + 1),
+ "Osmanya": range(66688, 66735 + 1),
+ "Osage": range(66736, 66815 + 1),
+ "Elbasan": range(66816, 66863 + 1),
+ "Caucasian Albanian": range(66864, 66927 + 1),
+ "Linear A": range(67072, 67455 + 1),
+ "Cypriot Syllabary": range(67584, 67647 + 1),
+ "Imperial Aramaic": range(67648, 67679 + 1),
+ "Palmyrene": range(67680, 67711 + 1),
+ "Nabataean": range(67712, 67759 + 1),
+ "Hatran": range(67808, 67839 + 1),
+ "Phoenician": range(67840, 67871 + 1),
+ "Lydian": range(67872, 67903 + 1),
+ "Meroitic Hieroglyphs": range(67968, 67999 + 1),
+ "Meroitic Cursive": range(68000, 68095 + 1),
+ "Kharoshthi": range(68096, 68191 + 1),
+ "Old South Arabian": range(68192, 68223 + 1),
+ "Old North Arabian": range(68224, 68255 + 1),
+ "Manichaean": range(68288, 68351 + 1),
+ "Avestan": range(68352, 68415 + 1),
+ "Inscriptional Parthian": range(68416, 68447 + 1),
+ "Inscriptional Pahlavi": range(68448, 68479 + 1),
+ "Psalter Pahlavi": range(68480, 68527 + 1),
+ "Old Turkic": range(68608, 68687 + 1),
+ "Old Hungarian": range(68736, 68863 + 1),
+ "Rumi Numeral Symbols": range(69216, 69247 + 1),
+ "Brahmi": range(69632, 69759 + 1),
+ "Kaithi": range(69760, 69839 + 1),
+ "Sora Sompeng": range(69840, 69887 + 1),
+ "Chakma": range(69888, 69967 + 1),
+ "Mahajani": range(69968, 70015 + 1),
+ "Sharada": range(70016, 70111 + 1),
+ "Sinhala Archaic Numbers": range(70112, 70143 + 1),
+ "Khojki": range(70144, 70223 + 1),
+ "Multani": range(70272, 70319 + 1),
+ "Khudawadi": range(70320, 70399 + 1),
+ "Grantha": range(70400, 70527 + 1),
+ "Newa": range(70656, 70783 + 1),
+ "Tirhuta": range(70784, 70879 + 1),
+ "Siddham": range(71040, 71167 + 1),
+ "Modi": range(71168, 71263 + 1),
+ "Mongolian Supplement": range(71264, 71295 + 1),
+ "Takri": range(71296, 71375 + 1),
+ "Ahom": range(71424, 71487 + 1),
+ "Warang Citi": range(71840, 71935 + 1),
+ "Zanabazar Square": range(72192, 72271 + 1),
+ "Soyombo": range(72272, 72367 + 1),
+ "Pau Cin Hau": range(72384, 72447 + 1),
+ "Bhaiksuki": range(72704, 72815 + 1),
+ "Marchen": range(72816, 72895 + 1),
+ "Masaram Gondi": range(72960, 73055 + 1),
+ "Cuneiform": range(73728, 74751 + 1),
+ "Cuneiform Numbers and Punctuation": range(74752, 74879 + 1),
+ "Early Dynastic Cuneiform": range(74880, 75087 + 1),
+ "Egyptian Hieroglyphs": range(77824, 78895 + 1),
+ "Anatolian Hieroglyphs": range(82944, 83583 + 1),
+ "Bamum Supplement": range(92160, 92735 + 1),
+ "Mro": range(92736, 92783 + 1),
+ "Bassa Vah": range(92880, 92927 + 1),
+ "Pahawh Hmong": range(92928, 93071 + 1),
+ "Miao": range(93952, 94111 + 1),
+ "Ideographic Symbols and Punctuation": range(94176, 94207 + 1),
+ "Tangut": range(94208, 100351 + 1),
+ "Tangut Components": range(100352, 101119 + 1),
+ "Kana Supplement": range(110592, 110847 + 1),
+ "Kana Extended-A": range(110848, 110895 + 1),
+ "Nushu": range(110960, 111359 + 1),
+ "Duployan": range(113664, 113823 + 1),
+ "Shorthand Format Controls": range(113824, 113839 + 1),
+ "Byzantine Musical Symbols": range(118784, 119039 + 1),
+ "Musical Symbols": range(119040, 119295 + 1),
+ "Ancient Greek Musical Notation": range(119296, 119375 + 1),
+ "Tai Xuan Jing Symbols": range(119552, 119647 + 1),
+ "Counting Rod Numerals": range(119648, 119679 + 1),
+ "Mathematical Alphanumeric Symbols": range(119808, 120831 + 1),
+ "Sutton SignWriting": range(120832, 121519 + 1),
+ "Glagolitic Supplement": range(122880, 122927 + 1),
+ "Mende Kikakui": range(124928, 125151 + 1),
+ "Adlam": range(125184, 125279 + 1),
+ "Arabic Mathematical Alphabetic Symbols": range(126464, 126719 + 1),
+ "Mahjong Tiles": range(126976, 127023 + 1),
+ "Domino Tiles": range(127024, 127135 + 1),
+ "Playing Cards": range(127136, 127231 + 1),
+ "Enclosed Alphanumeric Supplement": range(127232, 127487 + 1),
+ "Enclosed Ideographic Supplement": range(127488, 127743 + 1),
+ "Miscellaneous Symbols and Pictographs": range(127744, 128511 + 1),
+ "Emoticons range(Emoji)": range(128512, 128591 + 1),
+ "Ornamental Dingbats": range(128592, 128639 + 1),
+ "Transport and Map Symbols": range(128640, 128767 + 1),
+ "Alchemical Symbols": range(128768, 128895 + 1),
+ "Geometric Shapes Extended": range(128896, 129023 + 1),
+ "Supplemental Arrows-C": range(129024, 129279 + 1),
+ "Supplemental Symbols and Pictographs": range(129280, 129535 + 1),
+ "CJK Unified Ideographs Extension B": range(131072, 173791 + 1),
+ "CJK Unified Ideographs Extension C": range(173824, 177983 + 1),
+ "CJK Unified Ideographs Extension D": range(177984, 178207 + 1),
+ "CJK Unified Ideographs Extension E": range(178208, 183983 + 1),
+ "CJK Unified Ideographs Extension F": range(183984, 191471 + 1),
+ "CJK Compatibility Ideographs Supplement": range(194560, 195103 + 1),
+ "Tags": range(917504, 917631 + 1),
+ "Variation Selectors Supplement": range(917760, 917999 + 1),
+} # type: Dict[str, range]
UNICODE_SECONDARY_RANGE_KEYWORD = [
- 'Supplement',
- 'Extended',
- 'Extensions',
- 'Modifier',
- 'Marks',
- 'Punctuation',
- 'Symbols',
- 'Forms',
- 'Operators',
- 'Miscellaneous',
- 'Drawing',
- 'Block',
- 'Shapes',
- 'Supplemental',
- 'Tags'
+ "Supplement",
+ "Extended",
+ "Extensions",
+ "Modifier",
+ "Marks",
+ "Punctuation",
+ "Symbols",
+ "Forms",
+ "Operators",
+ "Miscellaneous",
+ "Drawing",
+ "Block",
+ "Shapes",
+ "Supplemental",
+ "Tags",
] # type: List[str]
RE_POSSIBLE_ENCODING_INDICATION = re_compile(
- r'(?:(?:encoding)|(?:charset)|(?:coding))(?:[\:= ]{1,10})(?:[\"\']?)([a-zA-Z0-9\-_]+)(?:[\"\']?)',
- IGNORECASE
+ r"(?:(?:encoding)|(?:charset)|(?:coding))(?:[\:= ]{1,10})(?:[\"\']?)([a-zA-Z0-9\-_]+)(?:[\"\']?)",
+ IGNORECASE,
)
IANA_SUPPORTED = sorted(
filter(
- lambda x: x.endswith("_codec") is False and x not in {"rot_13", "tactis", "mbcs"},
- list(set(aliases.values()))
+ lambda x: x.endswith("_codec") is False
+ and x not in {"rot_13", "tactis", "mbcs"},
+ list(set(aliases.values())),
)
) # type: List[str]
@@ -66,157 +346,39 @@ IANA_SUPPORTED_COUNT = len(IANA_SUPPORTED) # type: int
# pre-computed code page that are similar using the function cp_similarity.
IANA_SUPPORTED_SIMILAR = {
- "cp037": [
- "cp1026",
- "cp1140",
- "cp273",
- "cp500"
- ],
- "cp1026": [
- "cp037",
- "cp1140",
- "cp273",
- "cp500"
- ],
- "cp1125": [
- "cp866"
- ],
- "cp1140": [
- "cp037",
- "cp1026",
- "cp273",
- "cp500"
- ],
- "cp1250": [
- "iso8859_2"
- ],
- "cp1251": [
- "kz1048",
- "ptcp154"
- ],
- "cp1252": [
- "cp1258",
- "iso8859_15",
- "iso8859_9",
- "latin_1"
- ],
- "cp1253": [
- "iso8859_7"
- ],
- "cp1254": [
- "cp1258",
- "iso8859_15",
- "iso8859_9",
- "latin_1"
- ],
- "cp1257": [
- "iso8859_13"
- ],
- "cp1258": [
- "cp1252",
- "cp1254",
- "iso8859_9",
- "latin_1"
- ],
- "cp273": [
- "cp037",
- "cp1026",
- "cp1140",
- "cp500"
- ],
- "cp437": [
- "cp850",
- "cp858",
- "cp860",
- "cp861",
- "cp862",
- "cp863",
- "cp865"
- ],
- "cp500": [
- "cp037",
- "cp1026",
- "cp1140",
- "cp273"
- ],
- "cp850": [
- "cp437",
- "cp857",
- "cp858",
- "cp865"
- ],
- "cp857": [
- "cp850",
- "cp858",
- "cp865"
- ],
- "cp858": [
- "cp437",
- "cp850",
- "cp857",
- "cp865"
- ],
- "cp860": [
- "cp437",
- "cp861",
- "cp862",
- "cp863",
- "cp865"
- ],
- "cp861": [
- "cp437",
- "cp860",
- "cp862",
- "cp863",
- "cp865"
- ],
- "cp862": [
- "cp437",
- "cp860",
- "cp861",
- "cp863",
- "cp865"
- ],
- "cp863": [
- "cp437",
- "cp860",
- "cp861",
- "cp862",
- "cp865"
- ],
- "cp865": [
- "cp437",
- "cp850",
- "cp857",
- "cp858",
- "cp860",
- "cp861",
- "cp862",
- "cp863"
- ],
- "cp866": [
- "cp1125"
- ],
- "iso8859_10": [
- "iso8859_14",
- "iso8859_15",
- "iso8859_4",
- "iso8859_9",
- "latin_1"
- ],
- "iso8859_11": [
- "tis_620"
- ],
- "iso8859_13": [
- "cp1257"
- ],
+ "cp037": ["cp1026", "cp1140", "cp273", "cp500"],
+ "cp1026": ["cp037", "cp1140", "cp273", "cp500"],
+ "cp1125": ["cp866"],
+ "cp1140": ["cp037", "cp1026", "cp273", "cp500"],
+ "cp1250": ["iso8859_2"],
+ "cp1251": ["kz1048", "ptcp154"],
+ "cp1252": ["cp1258", "iso8859_15", "iso8859_9", "latin_1"],
+ "cp1253": ["iso8859_7"],
+ "cp1254": ["cp1258", "iso8859_15", "iso8859_9", "latin_1"],
+ "cp1257": ["iso8859_13"],
+ "cp1258": ["cp1252", "cp1254", "iso8859_9", "latin_1"],
+ "cp273": ["cp037", "cp1026", "cp1140", "cp500"],
+ "cp437": ["cp850", "cp858", "cp860", "cp861", "cp862", "cp863", "cp865"],
+ "cp500": ["cp037", "cp1026", "cp1140", "cp273"],
+ "cp850": ["cp437", "cp857", "cp858", "cp865"],
+ "cp857": ["cp850", "cp858", "cp865"],
+ "cp858": ["cp437", "cp850", "cp857", "cp865"],
+ "cp860": ["cp437", "cp861", "cp862", "cp863", "cp865"],
+ "cp861": ["cp437", "cp860", "cp862", "cp863", "cp865"],
+ "cp862": ["cp437", "cp860", "cp861", "cp863", "cp865"],
+ "cp863": ["cp437", "cp860", "cp861", "cp862", "cp865"],
+ "cp865": ["cp437", "cp850", "cp857", "cp858", "cp860", "cp861", "cp862", "cp863"],
+ "cp866": ["cp1125"],
+ "iso8859_10": ["iso8859_14", "iso8859_15", "iso8859_4", "iso8859_9", "latin_1"],
+ "iso8859_11": ["tis_620"],
+ "iso8859_13": ["cp1257"],
"iso8859_14": [
"iso8859_10",
"iso8859_15",
"iso8859_16",
"iso8859_3",
"iso8859_9",
- "latin_1"
+ "latin_1",
],
"iso8859_15": [
"cp1252",
@@ -226,7 +388,7 @@ IANA_SUPPORTED_SIMILAR = {
"iso8859_16",
"iso8859_3",
"iso8859_9",
- "latin_1"
+ "latin_1",
],
"iso8859_16": [
"iso8859_14",
@@ -234,29 +396,12 @@ IANA_SUPPORTED_SIMILAR = {
"iso8859_2",
"iso8859_3",
"iso8859_9",
- "latin_1"
- ],
- "iso8859_2": [
- "cp1250",
- "iso8859_16",
- "iso8859_4"
- ],
- "iso8859_3": [
- "iso8859_14",
- "iso8859_15",
- "iso8859_16",
- "iso8859_9",
- "latin_1"
- ],
- "iso8859_4": [
- "iso8859_10",
- "iso8859_2",
- "iso8859_9",
- "latin_1"
- ],
- "iso8859_7": [
- "cp1253"
+ "latin_1",
],
+ "iso8859_2": ["cp1250", "iso8859_16", "iso8859_4"],
+ "iso8859_3": ["iso8859_14", "iso8859_15", "iso8859_16", "iso8859_9", "latin_1"],
+ "iso8859_4": ["iso8859_10", "iso8859_2", "iso8859_9", "latin_1"],
+ "iso8859_7": ["cp1253"],
"iso8859_9": [
"cp1252",
"cp1254",
@@ -267,12 +412,9 @@ IANA_SUPPORTED_SIMILAR = {
"iso8859_16",
"iso8859_3",
"iso8859_4",
- "latin_1"
- ],
- "kz1048": [
- "cp1251",
- "ptcp154"
+ "latin_1",
],
+ "kz1048": ["cp1251", "ptcp154"],
"latin_1": [
"cp1252",
"cp1254",
@@ -283,61 +425,47 @@ IANA_SUPPORTED_SIMILAR = {
"iso8859_16",
"iso8859_3",
"iso8859_4",
- "iso8859_9"
+ "iso8859_9",
],
- "mac_iceland": [
- "mac_roman",
- "mac_turkish"
- ],
- "mac_roman": [
- "mac_iceland",
- "mac_turkish"
- ],
- "mac_turkish": [
- "mac_iceland",
- "mac_roman"
- ],
- "ptcp154": [
- "cp1251",
- "kz1048"
- ],
- "tis_620": [
- "iso8859_11"
- ]
+ "mac_iceland": ["mac_roman", "mac_turkish"],
+ "mac_roman": ["mac_iceland", "mac_turkish"],
+ "mac_turkish": ["mac_iceland", "mac_roman"],
+ "ptcp154": ["cp1251", "kz1048"],
+ "tis_620": ["iso8859_11"],
} # type: Dict[str, List[str]]
CHARDET_CORRESPONDENCE = {
- 'iso2022_kr': 'ISO-2022-KR',
- 'iso2022_jp': 'ISO-2022-JP',
- 'euc_kr': 'EUC-KR',
- 'tis_620': 'TIS-620',
- 'utf_32': 'UTF-32',
- 'euc_jp': 'EUC-JP',
- 'koi8_r': 'KOI8-R',
- 'iso8859_1': 'ISO-8859-1',
- 'iso8859_2': 'ISO-8859-2',
- 'iso8859_5': 'ISO-8859-5',
- 'iso8859_6': 'ISO-8859-6',
- 'iso8859_7': 'ISO-8859-7',
- 'iso8859_8': 'ISO-8859-8',
- 'utf_16': 'UTF-16',
- 'cp855': 'IBM855',
- 'mac_cyrillic': 'MacCyrillic',
- 'gb2312': 'GB2312',
- 'gb18030': 'GB18030',
- 'cp932': 'CP932',
- 'cp866': 'IBM866',
- 'utf_8': 'utf-8',
- 'utf_8_sig': 'UTF-8-SIG',
- 'shift_jis': 'SHIFT_JIS',
- 'big5': 'Big5',
- 'cp1250': 'windows-1250',
- 'cp1251': 'windows-1251',
- 'cp1252': 'Windows-1252',
- 'cp1253': 'windows-1253',
- 'cp1255': 'windows-1255',
- 'cp1256': 'windows-1256',
- 'cp1254': 'Windows-1254',
- 'cp949': 'CP949'
+ "iso2022_kr": "ISO-2022-KR",
+ "iso2022_jp": "ISO-2022-JP",
+ "euc_kr": "EUC-KR",
+ "tis_620": "TIS-620",
+ "utf_32": "UTF-32",
+ "euc_jp": "EUC-JP",
+ "koi8_r": "KOI8-R",
+ "iso8859_1": "ISO-8859-1",
+ "iso8859_2": "ISO-8859-2",
+ "iso8859_5": "ISO-8859-5",
+ "iso8859_6": "ISO-8859-6",
+ "iso8859_7": "ISO-8859-7",
+ "iso8859_8": "ISO-8859-8",
+ "utf_16": "UTF-16",
+ "cp855": "IBM855",
+ "mac_cyrillic": "MacCyrillic",
+ "gb2312": "GB2312",
+ "gb18030": "GB18030",
+ "cp932": "CP932",
+ "cp866": "IBM866",
+ "utf_8": "utf-8",
+ "utf_8_sig": "UTF-8-SIG",
+ "shift_jis": "SHIFT_JIS",
+ "big5": "Big5",
+ "cp1250": "windows-1250",
+ "cp1251": "windows-1251",
+ "cp1252": "Windows-1252",
+ "cp1253": "windows-1253",
+ "cp1255": "windows-1255",
+ "cp1256": "windows-1256",
+ "cp1254": "Windows-1254",
+ "cp949": "CP949",
} # type: Dict[str, str]
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/legacy.py b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/legacy.py
index 7b28cb48..71fa3cf8 100644
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/legacy.py
+++ b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/legacy.py
@@ -1,7 +1,10 @@
-from charset_normalizer.api import from_bytes
-from charset_normalizer.constant import CHARDET_CORRESPONDENCE
+import warnings
from typing import Dict, Optional, Union
+from .api import from_bytes, from_fp, from_path, normalize
+from .constant import CHARDET_CORRESPONDENCE
+from .models import CharsetMatch, CharsetMatches
+
def detect(byte_str: bytes) -> Dict[str, Optional[Union[str, float]]]:
"""
@@ -14,8 +17,10 @@ def detect(byte_str: bytes) -> Dict[str, Optional[Union[str, float]]]:
:param byte_str: The byte sequence to examine.
"""
if not isinstance(byte_str, (bytearray, bytes)):
- raise TypeError('Expected object of type bytes or bytearray, got: '
- '{0}'.format(type(byte_str)))
+ raise TypeError(
+ "Expected object of type bytes or bytearray, got: "
+ "{0}".format(type(byte_str))
+ )
if isinstance(byte_str, bytearray):
byte_str = bytes(byte_str)
@@ -23,16 +28,68 @@ def detect(byte_str: bytes) -> Dict[str, Optional[Union[str, float]]]:
r = from_bytes(byte_str).best()
encoding = r.encoding if r is not None else None
- language = r.language if r is not None and r.language != 'Unknown' else ''
- confidence = 1. - r.chaos if r is not None else None
+ language = r.language if r is not None and r.language != "Unknown" else ""
+ confidence = 1.0 - r.chaos if r is not None else None
# Note: CharsetNormalizer does not return 'UTF-8-SIG' as the sig get stripped in the detection/normalization process
# but chardet does return 'utf-8-sig' and it is a valid codec name.
- if r is not None and encoding == 'utf_8' and r.bom:
- encoding += '_sig'
+ if r is not None and encoding == "utf_8" and r.bom:
+ encoding += "_sig"
return {
- 'encoding': encoding if encoding not in CHARDET_CORRESPONDENCE else CHARDET_CORRESPONDENCE[encoding],
- 'language': language,
- 'confidence': confidence
+ "encoding": encoding
+ if encoding not in CHARDET_CORRESPONDENCE
+ else CHARDET_CORRESPONDENCE[encoding],
+ "language": language,
+ "confidence": confidence,
}
+
+
+class CharsetNormalizerMatch(CharsetMatch):
+ pass
+
+
+class CharsetNormalizerMatches(CharsetMatches):
+ @staticmethod
+ def from_fp(*args, **kwargs): # type: ignore
+ warnings.warn(
+ "staticmethod from_fp, from_bytes, from_path and normalize are deprecated "
+ "and scheduled to be removed in 3.0",
+ DeprecationWarning,
+ )
+ return from_fp(*args, **kwargs)
+
+ @staticmethod
+ def from_bytes(*args, **kwargs): # type: ignore
+ warnings.warn(
+ "staticmethod from_fp, from_bytes, from_path and normalize are deprecated "
+ "and scheduled to be removed in 3.0",
+ DeprecationWarning,
+ )
+ return from_bytes(*args, **kwargs)
+
+ @staticmethod
+ def from_path(*args, **kwargs): # type: ignore
+ warnings.warn(
+ "staticmethod from_fp, from_bytes, from_path and normalize are deprecated "
+ "and scheduled to be removed in 3.0",
+ DeprecationWarning,
+ )
+ return from_path(*args, **kwargs)
+
+ @staticmethod
+ def normalize(*args, **kwargs): # type: ignore
+ warnings.warn(
+ "staticmethod from_fp, from_bytes, from_path and normalize are deprecated "
+ "and scheduled to be removed in 3.0",
+ DeprecationWarning,
+ )
+ return normalize(*args, **kwargs)
+
+
+class CharsetDetector(CharsetNormalizerMatches):
+ pass
+
+
+class CharsetDoctor(CharsetNormalizerMatches):
+ pass
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/md.py b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/md.py
index ad6d737d..af3852b5 100644
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/md.py
+++ b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/md.py
@@ -1,9 +1,24 @@
from functools import lru_cache
-from typing import Optional, List
+from typing import List, Optional
-from charset_normalizer.constant import UNICODE_SECONDARY_RANGE_KEYWORD
-from charset_normalizer.utils import is_punctuation, is_symbol, unicode_range, is_accentuated, is_latin, \
- remove_accent, is_separator, is_cjk, is_case_variable, is_hangul, is_katakana, is_hiragana, is_ascii, is_thai
+from .constant import UNICODE_SECONDARY_RANGE_KEYWORD
+from .utils import (
+ is_accentuated,
+ is_ascii,
+ is_case_variable,
+ is_cjk,
+ is_emoticon,
+ is_hangul,
+ is_hiragana,
+ is_katakana,
+ is_latin,
+ is_punctuation,
+ is_separator,
+ is_symbol,
+ is_thai,
+ remove_accent,
+ unicode_range,
+)
class MessDetectorPlugin:
@@ -41,8 +56,7 @@ class MessDetectorPlugin:
class TooManySymbolOrPunctuationPlugin(MessDetectorPlugin):
-
- def __init__(self):
+ def __init__(self) -> None:
self._punctuation_count = 0 # type: int
self._symbol_count = 0 # type: int
self._character_count = 0 # type: int
@@ -56,10 +70,30 @@ class TooManySymbolOrPunctuationPlugin(MessDetectorPlugin):
def feed(self, character: str) -> None:
self._character_count += 1
- if character != self._last_printable_char and character not in ["<", ">", "=", ":", "/", "&", ";", "{", "}", "[", "]", ",", "|", '"']:
+ if character != self._last_printable_char and character not in [
+ "<",
+ ">",
+ "=",
+ ":",
+ "/",
+ "&",
+ ";",
+ "{",
+ "}",
+ "[",
+ "]",
+ ",",
+ "|",
+ '"',
+ "-",
+ ]:
if is_punctuation(character):
self._punctuation_count += 1
- elif character.isdigit() is False and is_symbol(character):
+ elif (
+ character.isdigit() is False
+ and is_symbol(character)
+ and is_emoticon(character) is False
+ ):
self._symbol_count += 2
self._last_printable_char = character
@@ -72,16 +106,17 @@ class TooManySymbolOrPunctuationPlugin(MessDetectorPlugin):
@property
def ratio(self) -> float:
if self._character_count == 0:
- return 0.
+ return 0.0
- ratio_of_punctuation = (self._punctuation_count + self._symbol_count) / self._character_count # type: float
+ ratio_of_punctuation = (
+ self._punctuation_count + self._symbol_count
+ ) / self._character_count # type: float
- return ratio_of_punctuation if ratio_of_punctuation >= 0.3 else 0.
+ return ratio_of_punctuation if ratio_of_punctuation >= 0.3 else 0.0
class TooManyAccentuatedPlugin(MessDetectorPlugin):
-
- def __init__(self):
+ def __init__(self) -> None:
self._character_count = 0 # type: int
self._accentuated_count = 0 # type: int
@@ -101,14 +136,15 @@ class TooManyAccentuatedPlugin(MessDetectorPlugin):
@property
def ratio(self) -> float:
if self._character_count == 0:
- return 0.
- ratio_of_accentuation = self._accentuated_count / self._character_count # type: float
- return ratio_of_accentuation if ratio_of_accentuation >= 0.35 else 0.
+ return 0.0
+ ratio_of_accentuation = (
+ self._accentuated_count / self._character_count
+ ) # type: float
+ return ratio_of_accentuation if ratio_of_accentuation >= 0.35 else 0.0
class UnprintablePlugin(MessDetectorPlugin):
-
- def __init__(self):
+ def __init__(self) -> None:
self._unprintable_count = 0 # type: int
self._character_count = 0 # type: int
@@ -116,7 +152,12 @@ class UnprintablePlugin(MessDetectorPlugin):
return True
def feed(self, character: str) -> None:
- if character not in {'\n', '\t', '\r', '\v'} and character.isprintable() is False:
+ if (
+ character not in {"\n", "\t", "\r", "\v"}
+ and character.isprintable() is False
+ and character.isspace() is False
+ and ord(character) != 0x1A # Why? Its the ASCII substitute character.
+ ):
self._unprintable_count += 1
self._character_count += 1
@@ -126,14 +167,13 @@ class UnprintablePlugin(MessDetectorPlugin):
@property
def ratio(self) -> float:
if self._character_count == 0:
- return 0.
+ return 0.0
return (self._unprintable_count * 8) / self._character_count
class SuspiciousDuplicateAccentPlugin(MessDetectorPlugin):
-
- def __init__(self):
+ def __init__(self) -> None:
self._successive_count = 0 # type: int
self._character_count = 0 # type: int
@@ -149,7 +189,9 @@ class SuspiciousDuplicateAccentPlugin(MessDetectorPlugin):
if character.isupper() and self._last_latin_character.isupper():
self._successive_count += 1
# Worse if its the same char duplicated with different accent.
- if remove_accent(character) == remove_accent(self._last_latin_character):
+ if remove_accent(character) == remove_accent(
+ self._last_latin_character
+ ):
self._successive_count += 1
self._last_latin_character = character
@@ -161,14 +203,13 @@ class SuspiciousDuplicateAccentPlugin(MessDetectorPlugin):
@property
def ratio(self) -> float:
if self._character_count == 0:
- return 0.
+ return 0.0
return (self._successive_count * 2) / self._character_count
class SuspiciousRange(MessDetectorPlugin):
-
- def __init__(self):
+ def __init__(self) -> None:
self._suspicious_successive_range_count = 0 # type: int
self._character_count = 0 # type: int
self._last_printable_seen = None # type: Optional[str]
@@ -179,7 +220,28 @@ class SuspiciousRange(MessDetectorPlugin):
def feed(self, character: str) -> None:
self._character_count += 1
- if character.isspace() or is_punctuation(character):
+ if (
+ character.isspace()
+ or is_punctuation(character)
+ or character
+ in [
+ "<",
+ ">",
+ "=",
+ ":",
+ "/",
+ "&",
+ ";",
+ "{",
+ "}",
+ "[",
+ "]",
+ ",",
+ "|",
+ '"',
+ "-",
+ ]
+ ):
self._last_printable_seen = None
return
@@ -187,7 +249,9 @@ class SuspiciousRange(MessDetectorPlugin):
self._last_printable_seen = character
return
- unicode_range_a = unicode_range(self._last_printable_seen) # type: Optional[str]
+ unicode_range_a = unicode_range(
+ self._last_printable_seen
+ ) # type: Optional[str]
unicode_range_b = unicode_range(character) # type: Optional[str]
if is_suspiciously_successive_range(unicode_range_a, unicode_range_b):
@@ -203,19 +267,20 @@ class SuspiciousRange(MessDetectorPlugin):
@property
def ratio(self) -> float:
if self._character_count == 0:
- return 0.
+ return 0.0
- ratio_of_suspicious_range_usage = (self._suspicious_successive_range_count * 2) / self._character_count # type: float
+ ratio_of_suspicious_range_usage = (
+ self._suspicious_successive_range_count * 2
+ ) / self._character_count # type: float
if ratio_of_suspicious_range_usage < 0.1:
- return 0.
+ return 0.0
return ratio_of_suspicious_range_usage
class SuperWeirdWordPlugin(MessDetectorPlugin):
-
- def __init__(self):
+ def __init__(self) -> None:
self._word_count = 0 # type: int
self._bad_word_count = 0 # type: int
self._is_current_word_bad = False # type: bool
@@ -235,12 +300,22 @@ class SuperWeirdWordPlugin(MessDetectorPlugin):
self._buffer = "".join([self._buffer, character])
if is_accentuated(character):
self._buffer_accent_count += 1
- if self._foreign_long_watch is False and is_latin(character) is False and is_cjk(character) is False and is_hangul(character) is False and is_katakana(character) is False and is_hiragana(character) is False and is_thai(character) is False:
+ if (
+ self._foreign_long_watch is False
+ and is_latin(character) is False
+ and is_cjk(character) is False
+ and is_hangul(character) is False
+ and is_katakana(character) is False
+ and is_hiragana(character) is False
+ and is_thai(character) is False
+ ):
self._foreign_long_watch = True
return
if not self._buffer:
return
- if (character.isspace() or is_punctuation(character) or is_separator(character)) and self._buffer:
+ if (
+ character.isspace() or is_punctuation(character) or is_separator(character)
+ ) and self._buffer:
self._word_count += 1
buffer_length = len(self._buffer) # type: int
@@ -259,7 +334,11 @@ class SuperWeirdWordPlugin(MessDetectorPlugin):
self._foreign_long_watch = False
self._buffer = ""
self._buffer_accent_count = 0
- elif character not in {"<", ">", "-", "="} and character.isdigit() is False and is_symbol(character):
+ elif (
+ character not in {"<", ">", "-", "="}
+ and character.isdigit() is False
+ and is_symbol(character)
+ ):
self._is_current_word_bad = True
self._buffer += character
@@ -275,18 +354,18 @@ class SuperWeirdWordPlugin(MessDetectorPlugin):
@property
def ratio(self) -> float:
if self._word_count <= 10:
- return 0.
+ return 0.0
return self._bad_character_count / self._character_count
class CjkInvalidStopPlugin(MessDetectorPlugin):
"""
- GB(Chinese) based encoding often render the stop incorrectly when the content does not fit and can be easily detected.
- Searching for the overuse of '丅' and '丄'.
+ GB(Chinese) based encoding often render the stop incorrectly when the content does not fit and
+ can be easily detected. Searching for the overuse of '丅' and '丄'.
"""
- def __init__(self):
+ def __init__(self) -> None:
self._wrong_stop_count = 0 # type: int
self._cjk_character_count = 0 # type: int
@@ -307,13 +386,12 @@ class CjkInvalidStopPlugin(MessDetectorPlugin):
@property
def ratio(self) -> float:
if self._cjk_character_count < 16:
- return 0.
+ return 0.0
return self._wrong_stop_count / self._cjk_character_count
class ArchaicUpperLowerPlugin(MessDetectorPlugin):
-
- def __init__(self):
+ def __init__(self) -> None:
self._buf = False # type: bool
self._character_count_since_last_sep = 0 # type: int
@@ -334,8 +412,14 @@ class ArchaicUpperLowerPlugin(MessDetectorPlugin):
chunk_sep = is_concerned is False
if chunk_sep and self._character_count_since_last_sep > 0:
- if self._character_count_since_last_sep <= 64 and character.isdigit() is False and self._current_ascii_only is False:
- self._successive_upper_lower_count_final += self._successive_upper_lower_count
+ if (
+ self._character_count_since_last_sep <= 64
+ and character.isdigit() is False
+ and self._current_ascii_only is False
+ ):
+ self._successive_upper_lower_count_final += (
+ self._successive_upper_lower_count
+ )
self._successive_upper_lower_count = 0
self._character_count_since_last_sep = 0
@@ -350,7 +434,9 @@ class ArchaicUpperLowerPlugin(MessDetectorPlugin):
self._current_ascii_only = False
if self._last_alpha_seen is not None:
- if (character.isupper() and self._last_alpha_seen.islower()) or (character.islower() and self._last_alpha_seen.isupper()):
+ if (character.isupper() and self._last_alpha_seen.islower()) or (
+ character.islower() and self._last_alpha_seen.isupper()
+ ):
if self._buf is True:
self._successive_upper_lower_count += 2
self._buf = False
@@ -375,12 +461,14 @@ class ArchaicUpperLowerPlugin(MessDetectorPlugin):
@property
def ratio(self) -> float:
if self._character_count == 0:
- return 0.
+ return 0.0
return self._successive_upper_lower_count_final / self._character_count
-def is_suspiciously_successive_range(unicode_range_a: Optional[str], unicode_range_b: Optional[str]) -> bool:
+def is_suspiciously_successive_range(
+ unicode_range_a: Optional[str], unicode_range_b: Optional[str]
+) -> bool:
"""
Determine if two Unicode range seen next to each other can be considered as suspicious.
"""
@@ -396,7 +484,9 @@ def is_suspiciously_successive_range(unicode_range_a: Optional[str], unicode_ran
if "Emoticons" in unicode_range_a or "Emoticons" in unicode_range_b:
return False
- keywords_range_a, keywords_range_b = unicode_range_a.split(" "), unicode_range_b.split(" ")
+ keywords_range_a, keywords_range_b = unicode_range_a.split(
+ " "
+ ), unicode_range_b.split(" ")
for el in keywords_range_a:
if el in UNICODE_SECONDARY_RANGE_KEYWORD:
@@ -405,10 +495,16 @@ def is_suspiciously_successive_range(unicode_range_a: Optional[str], unicode_ran
return False
# Japanese Exception
- if unicode_range_a in ['Katakana', 'Hiragana'] and unicode_range_b in ['Katakana', 'Hiragana']:
+ if unicode_range_a in ["Katakana", "Hiragana"] and unicode_range_b in [
+ "Katakana",
+ "Hiragana",
+ ]:
return False
- if unicode_range_a in ['Katakana', 'Hiragana'] or unicode_range_b in ['Katakana', 'Hiragana']:
+ if unicode_range_a in ["Katakana", "Hiragana"] or unicode_range_b in [
+ "Katakana",
+ "Hiragana",
+ ]:
if "CJK" in unicode_range_a or "CJK" in unicode_range_b:
return False
@@ -419,30 +515,33 @@ def is_suspiciously_successive_range(unicode_range_a: Optional[str], unicode_ran
return False
# Chinese/Japanese use dedicated range for punctuation and/or separators.
- if ('CJK' in unicode_range_a or 'CJK' in unicode_range_b) or (unicode_range_a in ['Katakana', 'Hiragana'] and unicode_range_b in ['Katakana', 'Hiragana']):
- if 'Punctuation' in unicode_range_a or 'Punctuation' in unicode_range_b:
+ if ("CJK" in unicode_range_a or "CJK" in unicode_range_b) or (
+ unicode_range_a in ["Katakana", "Hiragana"]
+ and unicode_range_b in ["Katakana", "Hiragana"]
+ ):
+ if "Punctuation" in unicode_range_a or "Punctuation" in unicode_range_b:
return False
- if 'Forms' in unicode_range_a or 'Forms' in unicode_range_b:
+ if "Forms" in unicode_range_a or "Forms" in unicode_range_b:
return False
return True
@lru_cache(maxsize=2048)
-def mess_ratio(decoded_sequence: str, maximum_threshold: float = 0.2, debug: bool = False) -> float:
+def mess_ratio(
+ decoded_sequence: str, maximum_threshold: float = 0.2, debug: bool = False
+) -> float:
"""
Compute a mess ratio given a decoded bytes sequence. The maximum threshold does stop the computation earlier.
"""
detectors = [] # type: List[MessDetectorPlugin]
for md_class in MessDetectorPlugin.__subclasses__():
- detectors.append(
- md_class()
- )
+ detectors.append(md_class())
length = len(decoded_sequence) # type: int
- mean_mess_ratio = 0. # type: float
+ mean_mess_ratio = 0.0 # type: float
if length < 512:
intermediary_mean_mess_ratio_calc = 32 # type: int
@@ -456,25 +555,16 @@ def mess_ratio(decoded_sequence: str, maximum_threshold: float = 0.2, debug: boo
if detector.eligible(character):
detector.feed(character)
- if (index > 0 and index % intermediary_mean_mess_ratio_calc == 0) or index == length-1:
- mean_mess_ratio = sum(
- [
- dt.ratio for dt in detectors
- ]
- )
+ if (
+ index > 0 and index % intermediary_mean_mess_ratio_calc == 0
+ ) or index == length - 1:
+ mean_mess_ratio = sum([dt.ratio for dt in detectors])
if mean_mess_ratio >= maximum_threshold:
break
if debug:
for dt in detectors: # pragma: nocover
- print(
- dt.__class__,
- dt.ratio
- )
-
- return round(
- mean_mess_ratio,
- 3
- )
+ print(dt.__class__, dt.ratio)
+ return round(mean_mess_ratio, 3)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/models.py b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/models.py
index bcea76ac..40d949dc 100644
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/models.py
+++ b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/models.py
@@ -1,25 +1,25 @@
import warnings
+from collections import Counter
from encodings.aliases import aliases
from hashlib import sha256
from json import dumps
-from typing import Optional, List, Tuple, Set
-from collections import Counter
-from re import sub, compile as re_compile
+from re import compile as re_compile, sub
+from typing import Any, Dict, Iterator, List, Optional, Set, Tuple, Union
-from charset_normalizer.constant import TOO_BIG_SEQUENCE
-from charset_normalizer.md import mess_ratio
-from charset_normalizer.utils import iana_name, is_multi_byte_encoding, unicode_range
+from .constant import TOO_BIG_SEQUENCE
+from .md import mess_ratio
+from .utils import iana_name, is_multi_byte_encoding, unicode_range
class CharsetMatch:
def __init__(
- self,
- payload: bytes,
- guessed_encoding: str,
- mean_mess_ratio: float,
- has_sig_or_bom: bool,
- languages: "CoherenceMatches",
- decoded_payload: Optional[str] = None
+ self,
+ payload: bytes,
+ guessed_encoding: str,
+ mean_mess_ratio: float,
+ has_sig_or_bom: bool,
+ languages: "CoherenceMatches",
+ decoded_payload: Optional[str] = None,
):
self._payload = payload # type: bytes
@@ -30,19 +30,23 @@ class CharsetMatch:
self._unicode_ranges = None # type: Optional[List[str]]
self._leaves = [] # type: List[CharsetMatch]
- self._mean_coherence_ratio = 0. # type: float
+ self._mean_coherence_ratio = 0.0 # type: float
self._output_payload = None # type: Optional[bytes]
self._output_encoding = None # type: Optional[str]
self._string = decoded_payload # type: Optional[str]
- def __eq__(self, other) -> bool:
+ def __eq__(self, other: object) -> bool:
if not isinstance(other, CharsetMatch):
- raise TypeError('__eq__ cannot be invoked on {} and {}.'.format(str(other.__class__), str(self.__class__)))
+ raise TypeError(
+ "__eq__ cannot be invoked on {} and {}.".format(
+ str(other.__class__), str(self.__class__)
+ )
+ )
return self.encoding == other.encoding and self.fingerprint == other.fingerprint
- def __lt__(self, other) -> bool:
+ def __lt__(self, other: object) -> bool:
"""
Implemented to make sorted available upon CharsetMatches items.
"""
@@ -53,10 +57,17 @@ class CharsetMatch:
# Bellow 1% difference --> Use Coherence
if chaos_difference < 0.01:
+ # When having a tough decision, use the result that decoded as many multi-byte as possible.
+ if chaos_difference == 0.0 and self.coherence == other.coherence:
+ return self.multi_byte_usage > other.multi_byte_usage
return self.coherence > other.coherence
return self.chaos < other.chaos
+ @property
+ def multi_byte_usage(self) -> float:
+ return 1.0 - len(str(self)) / len(self.raw)
+
@property
def chaos_secondary_pass(self) -> float:
"""
@@ -64,11 +75,11 @@ class CharsetMatch:
Use with caution, this can be very slow.
Notice: Will be removed in 3.0
"""
- warnings.warn("chaos_secondary_pass is deprecated and will be removed in 3.0", DeprecationWarning)
- return mess_ratio(
- str(self),
- 1.
+ warnings.warn(
+ "chaos_secondary_pass is deprecated and will be removed in 3.0",
+ DeprecationWarning,
)
+ return mess_ratio(str(self), 1.0)
@property
def coherence_non_latin(self) -> float:
@@ -76,8 +87,11 @@ class CharsetMatch:
Coherence ratio on the first non-latin language detected if ANY.
Notice: Will be removed in 3.0
"""
- warnings.warn("coherence_non_latin is deprecated and will be removed in 3.0", DeprecationWarning)
- return 0.
+ warnings.warn(
+ "coherence_non_latin is deprecated and will be removed in 3.0",
+ DeprecationWarning,
+ )
+ return 0.0
@property
def w_counter(self) -> Counter:
@@ -85,9 +99,11 @@ class CharsetMatch:
Word counter instance on decoded text.
Notice: Will be removed in 3.0
"""
- warnings.warn("w_counter is deprecated and will be removed in 3.0", DeprecationWarning)
- not_printable_pattern = re_compile(r'[0-9\W\n\r\t]+')
- string_printable_only = sub(not_printable_pattern, ' ', str(self).lower())
+ warnings.warn(
+ "w_counter is deprecated and will be removed in 3.0", DeprecationWarning
+ )
+ not_printable_pattern = re_compile(r"[0-9\W\n\r\t]+")
+ string_printable_only = sub(not_printable_pattern, " ", str(self).lower())
return Counter(string_printable_only.split())
@@ -102,7 +118,11 @@ class CharsetMatch:
def add_submatch(self, other: "CharsetMatch") -> None:
if not isinstance(other, CharsetMatch) or other == self:
- raise ValueError("Unable to add instance <{}> as a submatch of a CharsetMatch".format(other.__class__))
+ raise ValueError(
+ "Unable to add instance <{}> as a submatch of a CharsetMatch".format(
+ other.__class__
+ )
+ )
other._string = None # Unload RAM usage; dirty trick.
self._leaves.append(other)
@@ -153,9 +173,13 @@ class CharsetMatch:
return "English"
# doing it there to avoid circular import
- from charset_normalizer.cd import mb_encoding_languages, encoding_languages
+ from charset_normalizer.cd import encoding_languages, mb_encoding_languages
- languages = mb_encoding_languages(self.encoding) if is_multi_byte_encoding(self.encoding) else encoding_languages(self.encoding)
+ languages = (
+ mb_encoding_languages(self.encoding)
+ if is_multi_byte_encoding(self.encoding)
+ else encoding_languages(self.encoding)
+ )
if len(languages) == 0 or "Latin Based" in languages:
return "Unknown"
@@ -171,7 +195,7 @@ class CharsetMatch:
@property
def coherence(self) -> float:
if not self._languages:
- return 0.
+ return 0.0
return self._languages[0][1]
@property
@@ -205,9 +229,7 @@ class CharsetMatch:
for character in str(self):
detected_range = unicode_range(character) # type: Optional[str]
if detected_range:
- detected_ranges.add(
- unicode_range(character)
- )
+ detected_ranges.add(detected_range)
self._unicode_ranges = sorted(list(detected_ranges))
return self._unicode_ranges
@@ -256,14 +278,15 @@ class CharsetMatches:
Container with every CharsetMatch items ordered by default from most probable to the less one.
Act like a list(iterable) but does not implements all related methods.
"""
+
def __init__(self, results: List[CharsetMatch] = None):
self._results = sorted(results) if results else [] # type: List[CharsetMatch]
- def __iter__(self):
+ def __iter__(self) -> Iterator[CharsetMatch]:
for result in self._results:
yield result
- def __getitem__(self, item) -> CharsetMatch:
+ def __getitem__(self, item: Union[int, str]) -> CharsetMatch:
"""
Retrieve a single item either by its position or encoding name (alias may be used here).
Raise KeyError upon invalid index or encoding not present in results.
@@ -280,13 +303,20 @@ class CharsetMatches:
def __len__(self) -> int:
return len(self._results)
+ def __bool__(self) -> bool:
+ return len(self._results) > 0
+
def append(self, item: CharsetMatch) -> None:
"""
Insert a single match. Will be inserted accordingly to preserve sort.
Can be inserted as a submatch.
"""
if not isinstance(item, CharsetMatch):
- raise ValueError("Cannot append instance '{}' to CharsetMatches".format(str(item.__class__)))
+ raise ValueError(
+ "Cannot append instance '{}' to CharsetMatches".format(
+ str(item.__class__)
+ )
+ )
# We should disable the submatch factoring when the input file is too heavy (conserve RAM usage)
if len(item.raw) <= TOO_BIG_SEQUENCE:
for match in self._results:
@@ -316,11 +346,23 @@ CoherenceMatches = List[CoherenceMatch]
class CliDetectionResult:
-
- def __init__(self, path: str, encoding: str, encoding_aliases: List[str], alternative_encodings: List[str], language: str, alphabets: List[str], has_sig_or_bom: bool, chaos: float, coherence: float, unicode_path: Optional[str], is_preferred: bool):
+ def __init__(
+ self,
+ path: str,
+ encoding: Optional[str],
+ encoding_aliases: List[str],
+ alternative_encodings: List[str],
+ language: str,
+ alphabets: List[str],
+ has_sig_or_bom: bool,
+ chaos: float,
+ coherence: float,
+ unicode_path: Optional[str],
+ is_preferred: bool,
+ ):
self.path = path # type: str
self.unicode_path = unicode_path # type: Optional[str]
- self.encoding = encoding # type: str
+ self.encoding = encoding # type: Optional[str]
self.encoding_aliases = encoding_aliases # type: List[str]
self.alternative_encodings = alternative_encodings # type: List[str]
self.language = language # type: str
@@ -331,27 +373,20 @@ class CliDetectionResult:
self.is_preferred = is_preferred # type: bool
@property
- def __dict__(self):
+ def __dict__(self) -> Dict[str, Any]: # type: ignore
return {
- 'path': self.path,
- 'encoding': self.encoding,
- 'encoding_aliases': self.encoding_aliases,
- 'alternative_encodings': self.alternative_encodings,
- 'language': self.language,
- 'alphabets': self.alphabets,
- 'has_sig_or_bom': self.has_sig_or_bom,
- 'chaos': self.chaos,
- 'coherence': self.coherence,
- 'unicode_path': self.unicode_path,
- 'is_preferred': self.is_preferred
+ "path": self.path,
+ "encoding": self.encoding,
+ "encoding_aliases": self.encoding_aliases,
+ "alternative_encodings": self.alternative_encodings,
+ "language": self.language,
+ "alphabets": self.alphabets,
+ "has_sig_or_bom": self.has_sig_or_bom,
+ "chaos": self.chaos,
+ "coherence": self.coherence,
+ "unicode_path": self.unicode_path,
+ "is_preferred": self.is_preferred,
}
def to_json(self) -> str:
- return dumps(
- self.__dict__,
- ensure_ascii=True,
- indent=4
- )
-
-
-CharsetNormalizerMatch = CharsetMatch
+ return dumps(self.__dict__, ensure_ascii=True, indent=4)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/utils.py b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/utils.py
index 95bb2b1b..b9d12784 100644
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/utils.py
+++ b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/utils.py
@@ -1,19 +1,25 @@
try:
import unicodedata2 as unicodedata
except ImportError:
- import unicodedata
+ import unicodedata # type: ignore[no-redef]
-from codecs import IncrementalDecoder
-from re import findall
-from typing import Optional, Tuple, Union, List, Set
import importlib
-from _multibytecodec import MultibyteIncrementalDecoder # type: ignore
-
+from codecs import IncrementalDecoder
from encodings.aliases import aliases
from functools import lru_cache
+from re import findall
+from typing import List, Optional, Set, Tuple, Union
-from charset_normalizer.constant import UNICODE_RANGES_COMBINED, UNICODE_SECONDARY_RANGE_KEYWORD, \
- RE_POSSIBLE_ENCODING_INDICATION, ENCODING_MARKS, UTF8_MAXIMAL_ALLOCATION, IANA_SUPPORTED_SIMILAR
+from _multibytecodec import MultibyteIncrementalDecoder # type: ignore
+
+from .constant import (
+ ENCODING_MARKS,
+ IANA_SUPPORTED_SIMILAR,
+ RE_POSSIBLE_ENCODING_INDICATION,
+ UNICODE_RANGES_COMBINED,
+ UNICODE_SECONDARY_RANGE_KEYWORD,
+ UTF8_MAXIMAL_ALLOCATION,
+)
@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION)
@@ -22,7 +28,14 @@ def is_accentuated(character: str) -> bool:
description = unicodedata.name(character) # type: str
except ValueError:
return False
- return "WITH GRAVE" in description or "WITH ACUTE" in description or "WITH CEDILLA" in description or "WITH DIAERESIS" in description or "WITH CIRCUMFLEX" in description
+ return (
+ "WITH GRAVE" in description
+ or "WITH ACUTE" in description
+ or "WITH CEDILLA" in description
+ or "WITH DIAERESIS" in description
+ or "WITH CIRCUMFLEX" in description
+ or "WITH TILDE" in description
+ )
@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION)
@@ -33,12 +46,7 @@ def remove_accent(character: str) -> str:
codes = decomposed.split(" ") # type: List[str]
- return chr(
- int(
- codes[0],
- 16
- )
- )
+ return chr(int(codes[0], 16))
@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION)
@@ -71,6 +79,7 @@ def is_ascii(character: str) -> bool:
return False
return True
+
@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION)
def is_punctuation(character: str) -> bool:
character_category = unicodedata.category(character) # type: str
@@ -101,6 +110,16 @@ def is_symbol(character: str) -> bool:
return "Forms" in character_range
+@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION)
+def is_emoticon(character: str) -> bool:
+ character_range = unicode_range(character) # type: Optional[str]
+
+ if character_range is None:
+ return False
+
+ return "Emoticons" in character_range
+
+
@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION)
def is_separator(character: str) -> bool:
if character.isspace() or character in ["|", "+", ",", ";", "<", ">"]:
@@ -192,14 +211,16 @@ def any_specified_encoding(sequence: bytes, search_zone: int = 4096) -> Optional
results = findall(
RE_POSSIBLE_ENCODING_INDICATION,
- sequence[:seq_len if seq_len <= search_zone else search_zone].decode('ascii', errors='ignore')
+ sequence[: seq_len if seq_len <= search_zone else search_zone].decode(
+ "ascii", errors="ignore"
+ ),
) # type: List[str]
if len(results) == 0:
return None
for specified_encoding in results:
- specified_encoding = specified_encoding.lower().replace('-', '_')
+ specified_encoding = specified_encoding.lower().replace("-", "_")
for encoding_alias, encoding_iana in aliases.items():
if encoding_alias == specified_encoding:
@@ -215,9 +236,19 @@ def is_multi_byte_encoding(name: str) -> bool:
"""
Verify is a specific encoding is a multi byte one based on it IANA name
"""
- return name in {"utf_8", "utf_8_sig", "utf_16", "utf_16_be", "utf_16_le", "utf_32", "utf_32_le", "utf_32_be", "utf_7"} or issubclass(
- importlib.import_module('encodings.{}'.format(name)).IncrementalDecoder, # type: ignore
- MultibyteIncrementalDecoder
+ return name in {
+ "utf_8",
+ "utf_8_sig",
+ "utf_16",
+ "utf_16_be",
+ "utf_16_le",
+ "utf_32",
+ "utf_32_le",
+ "utf_32_be",
+ "utf_7",
+ } or issubclass(
+ importlib.import_module("encodings.{}".format(name)).IncrementalDecoder, # type: ignore
+ MultibyteIncrementalDecoder,
)
@@ -244,7 +275,7 @@ def should_strip_sig_or_bom(iana_encoding: str) -> bool:
def iana_name(cp_name: str, strict: bool = True) -> str:
- cp_name = cp_name.lower().replace('-', '_')
+ cp_name = cp_name.lower().replace("-", "_")
for encoding_alias, encoding_iana in aliases.items():
if cp_name == encoding_alias or cp_name == encoding_iana:
@@ -265,9 +296,7 @@ def range_scan(decoded_sequence: str) -> List[str]:
if character_range is None:
continue
- ranges.add(
- character_range
- )
+ ranges.add(character_range)
return list(ranges)
@@ -275,10 +304,10 @@ def range_scan(decoded_sequence: str) -> List[str]:
def cp_similarity(iana_name_a: str, iana_name_b: str) -> float:
if is_multi_byte_encoding(iana_name_a) or is_multi_byte_encoding(iana_name_b):
- return 0.
+ return 0.0
- decoder_a = importlib.import_module('encodings.{}'.format(iana_name_a)).IncrementalDecoder # type: ignore
- decoder_b = importlib.import_module('encodings.{}'.format(iana_name_b)).IncrementalDecoder # type: ignore
+ decoder_a = importlib.import_module("encodings.{}".format(iana_name_a)).IncrementalDecoder # type: ignore
+ decoder_b = importlib.import_module("encodings.{}".format(iana_name_b)).IncrementalDecoder # type: ignore
id_a = decoder_a(errors="ignore") # type: IncrementalDecoder
id_b = decoder_b(errors="ignore") # type: IncrementalDecoder
@@ -298,4 +327,7 @@ def is_cp_similar(iana_name_a: str, iana_name_b: str) -> bool:
Determine if two code page are at least 80% similar. IANA_SUPPORTED_SIMILAR dict was generated using
the function cp_similarity.
"""
- return iana_name_a in IANA_SUPPORTED_SIMILAR and iana_name_b in IANA_SUPPORTED_SIMILAR[iana_name_a]
+ return (
+ iana_name_a in IANA_SUPPORTED_SIMILAR
+ and iana_name_b in IANA_SUPPORTED_SIMILAR[iana_name_a]
+ )
diff --git a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/version.py b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/version.py
index 112ac0cc..bdba9fc2 100644
--- a/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/version.py
+++ b/IKEA_scraper/.venv/Lib/site-packages/charset_normalizer/version.py
@@ -2,5 +2,5 @@
Expose version
"""
-__version__ = "2.0.4"
-VERSION = __version__.split('.')
+__version__ = "2.0.6"
+VERSION = __version__.split(".")
diff --git a/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/__init__.cpython-39.pyc
index 690bcd1a..b53a633b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/__main__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/__main__.cpython-39.pyc
index 13727e61..97b23432 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/__main__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/__main__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/browsers.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/browsers.cpython-39.pyc
index 66491765..f7d73810 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/browsers.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/browsers.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/chrome.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/chrome.cpython-39.pyc
index 1fe24df0..e1ba719c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/chrome.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/chrome.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/edge.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/edge.cpython-39.pyc
index b7056671..43733d4a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/edge.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/edge.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/electron.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/electron.cpython-39.pyc
index 93ed2edb..730ed520 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/electron.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/eel/__pycache__/electron.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/__pycache__/__init__.cpython-39.pyc
index 8d0d0cbc..8729683e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/__init__.cpython-39.pyc
index 17f0d381..ff97a700 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/_markupbase.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/_markupbase.cpython-39.pyc
index b486d7d9..1786efeb 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/_markupbase.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/_markupbase.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/datetime.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/datetime.cpython-39.pyc
index 8726863d..c71e3a5d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/datetime.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/datetime.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/misc.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/misc.cpython-39.pyc
index 73879ca7..f271d02a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/misc.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/misc.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/socket.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/socket.cpython-39.pyc
index 55381cc8..d7e0d9c0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/socket.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/socket.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/socketserver.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/socketserver.cpython-39.pyc
index bfa16ff4..1bd41e3f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/socketserver.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/socketserver.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/total_ordering.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/total_ordering.cpython-39.pyc
index dec3ad7e..0c6ce1c2 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/total_ordering.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/__pycache__/total_ordering.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/__init__.cpython-39.pyc
index 0ff04051..045d317b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_encoded_words.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_encoded_words.cpython-39.pyc
index 9f412242..dccc661f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_encoded_words.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_encoded_words.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_header_value_parser.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_header_value_parser.cpython-39.pyc
index 7b73fda9..92972d41 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_header_value_parser.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_header_value_parser.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_parseaddr.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_parseaddr.cpython-39.pyc
index e1c0d2a6..87c41f72 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_parseaddr.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_parseaddr.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_policybase.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_policybase.cpython-39.pyc
index 11cded21..77be8202 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_policybase.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/_policybase.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/base64mime.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/base64mime.cpython-39.pyc
index 32781b17..93d03648 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/base64mime.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/base64mime.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/charset.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/charset.cpython-39.pyc
index ca125f5e..f84aa322 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/charset.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/charset.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/encoders.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/encoders.cpython-39.pyc
index 64e940d5..ab8c6b6b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/encoders.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/encoders.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/errors.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/errors.cpython-39.pyc
index dfe49159..50ee9bfc 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/errors.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/errors.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/feedparser.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/feedparser.cpython-39.pyc
index b77e73d4..9ce890c0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/feedparser.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/feedparser.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/generator.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/generator.cpython-39.pyc
index 4bb2494a..36b2aa5b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/generator.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/generator.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/header.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/header.cpython-39.pyc
index 46377e25..08d84d4a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/header.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/header.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/headerregistry.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/headerregistry.cpython-39.pyc
index e86450de..82ed32d7 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/headerregistry.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/headerregistry.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/iterators.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/iterators.cpython-39.pyc
index 0b295778..ad59653e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/iterators.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/iterators.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/message.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/message.cpython-39.pyc
index b0f98810..8349ed3e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/message.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/message.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/parser.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/parser.cpython-39.pyc
index 478d8395..dae69371 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/parser.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/parser.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/policy.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/policy.cpython-39.pyc
index caeef524..36b58355 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/policy.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/policy.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/quoprimime.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/quoprimime.cpython-39.pyc
index 8679e4fb..517aec20 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/quoprimime.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/quoprimime.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/utils.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/utils.cpython-39.pyc
index ebf13526..cd76ce7f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/utils.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/__pycache__/utils.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/__init__.cpython-39.pyc
index 37252010..b53a9bc4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/application.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/application.cpython-39.pyc
index aac77b2c..a6dd2cb5 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/application.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/application.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/audio.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/audio.cpython-39.pyc
index 7f25151f..aea52083 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/audio.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/audio.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/base.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/base.cpython-39.pyc
index b3081ab6..bed9f67d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/base.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/base.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/image.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/image.cpython-39.pyc
index 75aa798d..46e88e06 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/image.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/image.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/message.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/message.cpython-39.pyc
index 6d6ee87b..5a17b2b5 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/message.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/message.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/multipart.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/multipart.cpython-39.pyc
index 1a754d89..55615117 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/multipart.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/multipart.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/nonmultipart.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/nonmultipart.cpython-39.pyc
index 140da24a..75a1a938 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/nonmultipart.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/nonmultipart.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/text.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/text.cpython-39.pyc
index ddddaa02..1c5058b0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/text.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/email/mime/__pycache__/text.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/html/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/html/__pycache__/__init__.cpython-39.pyc
index c8f3c072..9d047f17 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/html/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/html/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/html/__pycache__/entities.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/html/__pycache__/entities.cpython-39.pyc
index c362996d..71a02362 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/html/__pycache__/entities.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/html/__pycache__/entities.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/html/__pycache__/parser.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/html/__pycache__/parser.cpython-39.pyc
index 364cb74f..9d639baf 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/html/__pycache__/parser.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/html/__pycache__/parser.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/__init__.cpython-39.pyc
index f7092276..a75f4a9c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/client.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/client.cpython-39.pyc
index 94e8496b..7e2ca339 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/client.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/client.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/cookiejar.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/cookiejar.cpython-39.pyc
index 3e98cffb..88c95d33 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/cookiejar.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/cookiejar.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/cookies.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/cookies.cpython-39.pyc
index 0c359841..3995a985 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/cookies.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/cookies.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/server.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/server.cpython-39.pyc
index 5802127b..cc41e53d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/server.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/http/__pycache__/server.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/__init__.cpython-39.pyc
index 78c24cbc..62b2f2d7 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/pystone.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/pystone.cpython-39.pyc
index 5f7ebc9d..18dc8670 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/pystone.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/pystone.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/ssl_servers.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/ssl_servers.cpython-39.pyc
index 5c0e5e93..63d59e86 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/ssl_servers.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/ssl_servers.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/support.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/support.cpython-39.pyc
index 29b10f58..36bb8d51 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/support.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/test/__pycache__/support.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/__init__.cpython-39.pyc
index 511b11ac..44a92df3 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/error.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/error.cpython-39.pyc
index 261f0bfa..22b5fb7f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/error.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/error.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/parse.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/parse.cpython-39.pyc
index ee811c81..75b7f634 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/parse.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/parse.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/request.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/request.cpython-39.pyc
index 383bbf5d..d90e8011 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/request.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/request.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/response.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/response.cpython-39.pyc
index e6679457..8a826916 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/response.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/response.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/robotparser.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/robotparser.cpython-39.pyc
index 26277303..0f64136f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/robotparser.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/urllib/__pycache__/robotparser.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/xmlrpc/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/xmlrpc/__pycache__/__init__.cpython-39.pyc
index 80bcf4d7..0f5ea5cf 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/xmlrpc/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/xmlrpc/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/xmlrpc/__pycache__/client.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/xmlrpc/__pycache__/client.cpython-39.pyc
index 14d722bc..af86bf3e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/xmlrpc/__pycache__/client.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/xmlrpc/__pycache__/client.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/backports/xmlrpc/__pycache__/server.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/backports/xmlrpc/__pycache__/server.cpython-39.pyc
index ae3f3030..aa637fd6 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/backports/xmlrpc/__pycache__/server.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/backports/xmlrpc/__pycache__/server.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/__init__.cpython-39.pyc
index 4928d69f..db70b4a3 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/disabled.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/disabled.cpython-39.pyc
index 9a88723f..cc0267bb 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/disabled.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/disabled.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/iterators.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/iterators.cpython-39.pyc
index 85484391..db638501 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/iterators.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/iterators.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/misc.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/misc.cpython-39.pyc
index 3164353f..3eae23ee 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/misc.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/misc.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/new_min_max.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/new_min_max.cpython-39.pyc
index 908a02b8..34986d23 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/new_min_max.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/new_min_max.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/newnext.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/newnext.cpython-39.pyc
index cf62aff4..a46f9255 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/newnext.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/newnext.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/newround.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/newround.cpython-39.pyc
index 6a461ffc..0f2df8dc 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/newround.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/newround.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/newsuper.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/newsuper.cpython-39.pyc
index 5f886b45..2ee1c970 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/newsuper.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/builtins/__pycache__/newsuper.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/__init__.cpython-39.pyc
index ca1f26e5..a3561eff 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/_dummy_thread.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/_dummy_thread.cpython-39.pyc
index 40cd56b5..075e0942 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/_dummy_thread.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/_dummy_thread.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/_markupbase.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/_markupbase.cpython-39.pyc
index c940dd31..13df3eca 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/_markupbase.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/_markupbase.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/_thread.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/_thread.cpython-39.pyc
index de10243d..f1250dd2 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/_thread.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/_thread.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/builtins.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/builtins.cpython-39.pyc
index f2bb7faa..24347bc4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/builtins.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/builtins.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/collections.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/collections.cpython-39.pyc
index 113a6421..01deb537 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/collections.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/collections.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/configparser.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/configparser.cpython-39.pyc
index fd46f299..655c6b28 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/configparser.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/configparser.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/copyreg.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/copyreg.cpython-39.pyc
index 571cd4e7..45a93f41 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/copyreg.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/copyreg.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/itertools.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/itertools.cpython-39.pyc
index 799d8b23..ea55f54f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/itertools.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/itertools.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/pickle.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/pickle.cpython-39.pyc
index 407418d6..460719b6 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/pickle.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/pickle.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/queue.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/queue.cpython-39.pyc
index da96b354..252e27e5 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/queue.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/queue.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/reprlib.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/reprlib.cpython-39.pyc
index 0067877b..594e82e3 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/reprlib.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/reprlib.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/socketserver.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/socketserver.cpython-39.pyc
index 28f1a9ec..4506925b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/socketserver.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/socketserver.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/subprocess.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/subprocess.cpython-39.pyc
index 11f8c9f4..bc18a74e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/subprocess.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/subprocess.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/sys.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/sys.cpython-39.pyc
index e752933b..332bfbf2 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/sys.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/sys.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/winreg.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/winreg.cpython-39.pyc
index 13fb87ad..8b1af919 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/winreg.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/__pycache__/winreg.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/__init__.cpython-39.pyc
index bb384a92..7920abe0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/dumb.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/dumb.cpython-39.pyc
index 7c92a6c6..c76308b1 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/dumb.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/dumb.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/gnu.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/gnu.cpython-39.pyc
index 6213813f..c262f53f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/gnu.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/gnu.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/ndbm.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/ndbm.cpython-39.pyc
index c14b317f..27998b74 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/ndbm.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/dbm/__pycache__/ndbm.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/html/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/html/__pycache__/__init__.cpython-39.pyc
index f28752c8..db13d97f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/html/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/html/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/html/__pycache__/entities.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/html/__pycache__/entities.cpython-39.pyc
index dc480742..a6be492e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/html/__pycache__/entities.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/html/__pycache__/entities.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/html/__pycache__/parser.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/html/__pycache__/parser.cpython-39.pyc
index 38cbfa89..c00e9096 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/html/__pycache__/parser.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/html/__pycache__/parser.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/__init__.cpython-39.pyc
index cbfe23cd..f77aae1f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/client.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/client.cpython-39.pyc
index 5efabe23..7d03b669 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/client.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/client.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/cookiejar.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/cookiejar.cpython-39.pyc
index b0464b99..9eb9a58e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/cookiejar.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/cookiejar.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/cookies.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/cookies.cpython-39.pyc
index c04fce4d..26440c03 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/cookies.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/cookies.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/server.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/server.cpython-39.pyc
index abd06b42..63e6e3ca 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/server.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/http/__pycache__/server.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/test/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/test/__pycache__/__init__.cpython-39.pyc
index 12696904..19e9916f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/test/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/test/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/test/__pycache__/support.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/test/__pycache__/support.cpython-39.pyc
index 05f833d9..229c536a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/test/__pycache__/support.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/test/__pycache__/support.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/__init__.cpython-39.pyc
index 80ee1bae..86259476 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/colorchooser.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/colorchooser.cpython-39.pyc
index b54ec31a..36e86077 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/colorchooser.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/colorchooser.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/commondialog.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/commondialog.cpython-39.pyc
index 727b6d83..62289dcf 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/commondialog.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/commondialog.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/constants.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/constants.cpython-39.pyc
index c9b5cd72..d04214ac 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/constants.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/constants.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/dialog.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/dialog.cpython-39.pyc
index 2989ffc6..577be5da 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/dialog.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/dialog.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/dnd.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/dnd.cpython-39.pyc
index f4fa31a3..7effdbb5 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/dnd.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/dnd.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/filedialog.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/filedialog.cpython-39.pyc
index fa58205e..f15bf36e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/filedialog.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/filedialog.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/font.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/font.cpython-39.pyc
index 678fc615..89a0cfa4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/font.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/font.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/messagebox.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/messagebox.cpython-39.pyc
index 99116bd3..b5617bb5 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/messagebox.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/messagebox.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/scrolledtext.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/scrolledtext.cpython-39.pyc
index 8dad8cbc..d83a092d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/scrolledtext.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/scrolledtext.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/simpledialog.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/simpledialog.cpython-39.pyc
index 0eb7494c..d51ce1ac 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/simpledialog.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/simpledialog.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/tix.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/tix.cpython-39.pyc
index bd57b3a3..76f8f6cd 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/tix.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/tix.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/ttk.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/ttk.cpython-39.pyc
index 9a76cfc1..88203937 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/ttk.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/tkinter/__pycache__/ttk.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/__init__.cpython-39.pyc
index 5900ac9f..fcf7adff 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/error.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/error.cpython-39.pyc
index 60f67932..5abccee4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/error.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/error.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/parse.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/parse.cpython-39.pyc
index 5d6633a7..b3cdd0b4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/parse.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/parse.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/request.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/request.cpython-39.pyc
index b1b0e639..d989166b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/request.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/request.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/response.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/response.cpython-39.pyc
index 71da5a9f..ac5343ed 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/response.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/response.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/robotparser.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/robotparser.cpython-39.pyc
index 7df2d6ba..331f5359 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/robotparser.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/urllib/__pycache__/robotparser.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/xmlrpc/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/xmlrpc/__pycache__/__init__.cpython-39.pyc
index b85917ba..b060ede0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/xmlrpc/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/xmlrpc/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/xmlrpc/__pycache__/client.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/xmlrpc/__pycache__/client.cpython-39.pyc
index 3822be3a..c744a40d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/xmlrpc/__pycache__/client.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/xmlrpc/__pycache__/client.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/moves/xmlrpc/__pycache__/server.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/moves/xmlrpc/__pycache__/server.cpython-39.pyc
index 1b60eac5..b35876a1 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/moves/xmlrpc/__pycache__/server.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/moves/xmlrpc/__pycache__/server.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/standard_library/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/standard_library/__pycache__/__init__.cpython-39.pyc
index 04a9fec8..fa2f112e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/standard_library/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/standard_library/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/tests/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/tests/__pycache__/__init__.cpython-39.pyc
index b3131aed..5c4a2e84 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/tests/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/tests/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/tests/__pycache__/base.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/tests/__pycache__/base.cpython-39.pyc
index a1ed1818..d1fb12b6 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/tests/__pycache__/base.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/tests/__pycache__/base.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/__init__.cpython-39.pyc
index 8ca04c84..48ccca3c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newbytes.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newbytes.cpython-39.pyc
index 9b60cd34..00956f08 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newbytes.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newbytes.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newdict.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newdict.cpython-39.pyc
index 5c500155..3dc1cd35 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newdict.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newdict.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newint.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newint.cpython-39.pyc
index c302148c..37cdd24b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newint.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newint.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newlist.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newlist.cpython-39.pyc
index d7f9b4b0..01d9134e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newlist.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newlist.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newmemoryview.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newmemoryview.cpython-39.pyc
index 11181078..744cafe5 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newmemoryview.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newmemoryview.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newobject.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newobject.cpython-39.pyc
index 28162925..711b2046 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newobject.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newobject.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newopen.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newopen.cpython-39.pyc
index a13f5a98..01ee94b0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newopen.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newopen.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newrange.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newrange.cpython-39.pyc
index bfbfd2b7..88e687d4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newrange.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newrange.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newstr.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newstr.cpython-39.pyc
index d745dc7f..69abfb58 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newstr.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/types/__pycache__/newstr.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/utils/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/utils/__pycache__/__init__.cpython-39.pyc
index 192c1a7b..add145c0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/utils/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/utils/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/future/utils/__pycache__/surrogateescape.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/future/utils/__pycache__/surrogateescape.cpython-39.pyc
index 15f0b31f..c277226b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/future/utils/__pycache__/surrogateescape.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/future/utils/__pycache__/surrogateescape.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/__init__.cpython-39.pyc
index 6f0dbded..f8c01def 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_abstract_linkable.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_abstract_linkable.cpython-39.pyc
index 8ba338af..3d79533e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_abstract_linkable.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_abstract_linkable.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_compat.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_compat.cpython-39.pyc
index 6f05de6c..1ab3bacb 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_compat.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_compat.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_config.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_config.cpython-39.pyc
index c49e4d82..9fac9327 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_config.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_config.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_fileobjectcommon.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_fileobjectcommon.cpython-39.pyc
index 2ce17127..a18dd765 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_fileobjectcommon.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_fileobjectcommon.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_fileobjectposix.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_fileobjectposix.cpython-39.pyc
index 5b01a20a..b7d0eeaa 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_fileobjectposix.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_fileobjectposix.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_greenlet_primitives.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_greenlet_primitives.cpython-39.pyc
index 817fa967..b948a47e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_greenlet_primitives.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_greenlet_primitives.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_hub_local.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_hub_local.cpython-39.pyc
index 862048c1..93284850 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_hub_local.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_hub_local.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_hub_primitives.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_hub_primitives.cpython-39.pyc
index 0c121036..3c65b481 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_hub_primitives.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_hub_primitives.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_ident.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_ident.cpython-39.pyc
index 0c4b7ec7..e8091c5f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_ident.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_ident.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_imap.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_imap.cpython-39.pyc
index b5a274f0..42b104ad 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_imap.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_imap.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_interfaces.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_interfaces.cpython-39.pyc
index 4378ef9f..0277cf7c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_interfaces.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_interfaces.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_monitor.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_monitor.cpython-39.pyc
index b49ecbbd..94eaccb1 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_monitor.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_monitor.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_patcher.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_patcher.cpython-39.pyc
index 36fc4508..11345241 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_patcher.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_patcher.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_semaphore.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_semaphore.cpython-39.pyc
index b53144e1..e480f93a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_semaphore.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_semaphore.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_socket2.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_socket2.cpython-39.pyc
index c3d4be50..3114cb3f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_socket2.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_socket2.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_socket3.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_socket3.cpython-39.pyc
index f517a2f6..41d81725 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_socket3.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_socket3.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_socketcommon.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_socketcommon.cpython-39.pyc
index a7bad5e3..1c47f91c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_socketcommon.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_socketcommon.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_ssl2.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_ssl2.cpython-39.pyc
index 8b20a3ed..f5ea6f8e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_ssl2.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_ssl2.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_ssl3.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_ssl3.cpython-39.pyc
index 1f51b5b8..7dbf738b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_ssl3.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_ssl3.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_sslgte279.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_sslgte279.cpython-39.pyc
index 626423f3..e9ce0c4e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_sslgte279.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_sslgte279.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_tblib.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_tblib.cpython-39.pyc
index 96d2a730..cb398cc7 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_tblib.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_tblib.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_threading.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_threading.cpython-39.pyc
index 2b98801d..d669fc65 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_threading.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_threading.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_tracer.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_tracer.cpython-39.pyc
index 11711b89..6c632374 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_tracer.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_tracer.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_util.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_util.cpython-39.pyc
index 8d603b70..a01e1193 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_util.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_util.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_util_py2.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_util_py2.cpython-39.pyc
index f3075658..f022ba66 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_util_py2.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_util_py2.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_waiter.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_waiter.cpython-39.pyc
index 071cd5ce..dec0247e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_waiter.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/_waiter.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/ares.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/ares.cpython-39.pyc
index 97700540..64c641b5 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/ares.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/ares.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/backdoor.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/backdoor.cpython-39.pyc
index b58ba671..60551ee7 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/backdoor.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/backdoor.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/baseserver.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/baseserver.cpython-39.pyc
index 41e9a624..93ecba43 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/baseserver.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/baseserver.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/builtins.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/builtins.cpython-39.pyc
index 80523ad2..0bd99a0d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/builtins.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/builtins.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/contextvars.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/contextvars.cpython-39.pyc
index 36f0814f..5e24f429 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/contextvars.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/contextvars.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/core.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/core.cpython-39.pyc
index af6ec0fa..7f45a5cb 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/core.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/core.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/event.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/event.cpython-39.pyc
index 611c9b81..0f7fda6a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/event.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/event.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/events.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/events.cpython-39.pyc
index 5fbaf1df..a931ebc5 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/events.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/events.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/exceptions.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/exceptions.cpython-39.pyc
index e7dae5c3..d434c8ca 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/exceptions.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/exceptions.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/fileobject.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/fileobject.cpython-39.pyc
index 5ab1609c..c1a3030d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/fileobject.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/fileobject.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/greenlet.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/greenlet.cpython-39.pyc
index dc744cd7..fcc7c26c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/greenlet.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/greenlet.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/hub.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/hub.cpython-39.pyc
index 83a2547e..342e9f26 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/hub.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/hub.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/local.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/local.cpython-39.pyc
index 3431f45e..b4948090 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/local.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/local.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/lock.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/lock.cpython-39.pyc
index d7d517ad..5e7fe960 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/lock.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/lock.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/monkey.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/monkey.cpython-39.pyc
index 912eaa96..8d97fff8 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/monkey.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/monkey.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/os.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/os.cpython-39.pyc
index 31981d60..5759a12c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/os.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/os.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/pool.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/pool.cpython-39.pyc
index 96f4456c..90e2ac26 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/pool.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/pool.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/pywsgi.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/pywsgi.cpython-39.pyc
index c03230dd..b0aa6b6c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/pywsgi.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/pywsgi.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/queue.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/queue.cpython-39.pyc
index 19c3b8b7..c6f6bd7a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/queue.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/queue.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/resolver_ares.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/resolver_ares.cpython-39.pyc
index f60030c8..1864d8f3 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/resolver_ares.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/resolver_ares.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/resolver_thread.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/resolver_thread.cpython-39.pyc
index 59325cd5..96e787a6 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/resolver_thread.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/resolver_thread.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/select.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/select.cpython-39.pyc
index d18891bd..383deadd 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/select.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/select.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/selectors.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/selectors.cpython-39.pyc
index ce13585e..956961e4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/selectors.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/selectors.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/server.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/server.cpython-39.pyc
index a1b8c9d7..fd3cd1c3 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/server.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/server.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/signal.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/signal.cpython-39.pyc
index f6e73cdd..05396ffc 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/signal.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/signal.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/socket.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/socket.cpython-39.pyc
index bd4a5eba..52817fb7 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/socket.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/socket.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/ssl.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/ssl.cpython-39.pyc
index a2560d06..cfe3cced 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/ssl.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/ssl.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/subprocess.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/subprocess.cpython-39.pyc
index 0aaa36dd..c408ceea 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/subprocess.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/subprocess.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/thread.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/thread.cpython-39.pyc
index f21a45fe..077beb0e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/thread.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/thread.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/threading.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/threading.cpython-39.pyc
index 313ab6ab..061c7c2f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/threading.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/threading.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/threadpool.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/threadpool.cpython-39.pyc
index d4d8cd2f..9818f7ab 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/threadpool.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/threadpool.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/time.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/time.cpython-39.pyc
index 7e329ef1..2c3853cd 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/time.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/time.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/timeout.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/timeout.cpython-39.pyc
index 351aa55f..23b50a51 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/timeout.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/timeout.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/util.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/util.cpython-39.pyc
index 3ac57d71..ad866085 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/util.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/util.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/win32util.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/win32util.cpython-39.pyc
index 8da9410a..6e7d72e9 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/win32util.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/__pycache__/win32util.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/__init__.cpython-39.pyc
index 0ac33a09..dcdca1a3 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/callback.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/callback.cpython-39.pyc
index bb46d666..0584a488 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/callback.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/callback.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/loop.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/loop.cpython-39.pyc
index d94d45a5..52685b7a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/loop.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/loop.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/watcher.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/watcher.cpython-39.pyc
index 927819ec..8787af18 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/watcher.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/_ffi/__pycache__/watcher.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/__init__.cpython-39.pyc
index dac44e13..97145111 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/_corecffi_build.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/_corecffi_build.cpython-39.pyc
index da1a8784..33606988 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/_corecffi_build.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/_corecffi_build.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/corecffi.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/corecffi.cpython-39.pyc
index 31f0e3cd..97e3688e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/corecffi.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/corecffi.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/watcher.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/watcher.cpython-39.pyc
index c6e6ef9f..a1f8d731 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/watcher.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/libev/__pycache__/watcher.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/__init__.cpython-39.pyc
index ff8b6bf3..c58c96c8 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/_corecffi_build.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/_corecffi_build.cpython-39.pyc
index 43295e69..25581da1 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/_corecffi_build.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/_corecffi_build.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/loop.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/loop.cpython-39.pyc
index 089b5aae..6070c682 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/loop.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/loop.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/watcher.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/watcher.cpython-39.pyc
index 5567e90c..deabbb19 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/watcher.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/libuv/__pycache__/watcher.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/__init__.cpython-39.pyc
index dbf71368..0b810b41 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/_addresses.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/_addresses.cpython-39.pyc
index dc06d7ef..2a3a1ea4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/_addresses.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/_addresses.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/_hostsfile.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/_hostsfile.cpython-39.pyc
index 311ce60d..c75ec90d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/_hostsfile.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/_hostsfile.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/ares.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/ares.cpython-39.pyc
index 35b5f576..eb2cd125 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/ares.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/ares.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/blocking.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/blocking.cpython-39.pyc
index f1f03c3e..758a419e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/blocking.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/blocking.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/dnspython.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/dnspython.cpython-39.pyc
index 1f8ba1eb..060c49fc 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/dnspython.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/dnspython.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/thread.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/thread.cpython-39.pyc
index 3ae54f17..6ebb7452 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/thread.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/resolver/__pycache__/thread.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/__init__.cpython-39.pyc
index 169b6761..b4e54e80 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/errorhandler.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/errorhandler.cpython-39.pyc
index d5d40487..b02e7344 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/errorhandler.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/errorhandler.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/exception.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/exception.cpython-39.pyc
index e82d238b..3a5604a5 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/exception.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/exception.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/flaky.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/flaky.cpython-39.pyc
index fe96eab3..94309e65 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/flaky.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/flaky.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/hub.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/hub.cpython-39.pyc
index 391dfb8b..2284607d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/hub.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/hub.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/leakcheck.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/leakcheck.cpython-39.pyc
index 8435b4d7..b13c1077 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/leakcheck.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/leakcheck.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/modules.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/modules.cpython-39.pyc
index 7733ce2e..d6d2bcb9 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/modules.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/modules.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/monkey_test.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/monkey_test.cpython-39.pyc
index 1b135cd7..9eb898cf 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/monkey_test.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/monkey_test.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/openfiles.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/openfiles.cpython-39.pyc
index f8dc6a5b..8515319c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/openfiles.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/openfiles.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/params.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/params.cpython-39.pyc
index b08de84c..c5e2d693 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/params.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/params.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/patched_tests_setup.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/patched_tests_setup.cpython-39.pyc
index 3488e5f9..b824bbe7 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/patched_tests_setup.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/patched_tests_setup.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/resources.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/resources.cpython-39.pyc
index 63a6b930..f3e3e032 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/resources.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/resources.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/six.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/six.cpython-39.pyc
index 86c94bbb..59e89bc8 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/six.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/six.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/skipping.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/skipping.cpython-39.pyc
index 9ca2c017..7ba0810d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/skipping.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/skipping.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/sockets.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/sockets.cpython-39.pyc
index fe716897..9319b9cb 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/sockets.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/sockets.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/support.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/support.cpython-39.pyc
index 78b83655..407c8e6b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/support.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/support.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/switching.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/switching.cpython-39.pyc
index 183830d6..c8a98a88 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/switching.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/switching.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/sysinfo.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/sysinfo.cpython-39.pyc
index 58506d36..b2fd681d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/sysinfo.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/sysinfo.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/testcase.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/testcase.cpython-39.pyc
index 014b9e3d..be00d6cb 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/testcase.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/testcase.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/testrunner.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/testrunner.cpython-39.pyc
index fd31a87a..079bf567 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/testrunner.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/testrunner.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/timing.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/timing.cpython-39.pyc
index f6d48005..c289a187 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/timing.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/timing.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/travis.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/travis.cpython-39.pyc
index 62410569..6436a6de 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/travis.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/travis.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/util.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/util.cpython-39.pyc
index 25666688..159c4042 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/util.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/__pycache__/util.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/coveragesite/__pycache__/sitecustomize.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/coveragesite/__pycache__/sitecustomize.cpython-39.pyc
index f1a3a295..4c5ebeef 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/coveragesite/__pycache__/sitecustomize.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/testing/coveragesite/__pycache__/sitecustomize.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/__init__.cpython-39.pyc
index 7db021e8..559d0737 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/__main__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/__main__.cpython-39.pyc
index f30f551b..38eb3670 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/__main__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/__main__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_blocks_at_top_level.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_blocks_at_top_level.cpython-39.pyc
index 0e3a17ae..755c6d6f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_blocks_at_top_level.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_blocks_at_top_level.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_import_import_patch.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_import_import_patch.cpython-39.pyc
index 02238854..efa18c91 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_import_import_patch.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_import_import_patch.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_import_patch.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_import_patch.cpython-39.pyc
index 83afaf58..39e6900c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_import_patch.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_import_patch.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_import_wait.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_import_wait.cpython-39.pyc
index fd9c62f5..e19f9df8 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_import_wait.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_import_wait.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_imports_at_top_level.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_imports_at_top_level.cpython-39.pyc
index 701d3f2d..5d74866d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_imports_at_top_level.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_imports_at_top_level.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_imports_imports_at_top_level.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_imports_imports_at_top_level.cpython-39.pyc
index a6fb91a6..9ce39a52 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_imports_imports_at_top_level.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/_imports_imports_at_top_level.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/getaddrinfo_module.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/getaddrinfo_module.cpython-39.pyc
index e746d501..b30c4b8a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/getaddrinfo_module.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/getaddrinfo_module.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/known_failures.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/known_failures.cpython-39.pyc
index 4f4269c0..d970de34 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/known_failures.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/known_failures.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/lock_tests.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/lock_tests.cpython-39.pyc
index 5549bc7b..e19445e5 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/lock_tests.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/lock_tests.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__GreenletExit.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__GreenletExit.cpython-39.pyc
index 622f5bed..adf1412e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__GreenletExit.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__GreenletExit.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___config.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___config.cpython-39.pyc
index 81e04ba6..f6425452 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___config.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___config.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___ident.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___ident.cpython-39.pyc
index 29c8a41c..7fd359ce 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___ident.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___ident.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___monitor.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___monitor.cpython-39.pyc
index 2dda2aa6..7d7ed9d4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___monitor.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___monitor.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___monkey_patching.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___monkey_patching.cpython-39.pyc
index 1e5e5564..a3869aea 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___monkey_patching.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test___monkey_patching.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__all__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__all__.cpython-39.pyc
index 246fa7b9..5ed357dd 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__all__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__all__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__api.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__api.cpython-39.pyc
index b86a7fae..eb4f9ab8 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__api.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__api.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__api_timeout.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__api_timeout.cpython-39.pyc
index b43f34d2..871dff21 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__api_timeout.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__api_timeout.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__ares_host_result.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__ares_host_result.cpython-39.pyc
index 04d35355..9069aed6 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__ares_host_result.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__ares_host_result.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__ares_timeout.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__ares_timeout.cpython-39.pyc
index 198b9920..42e34c91 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__ares_timeout.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__ares_timeout.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__backdoor.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__backdoor.cpython-39.pyc
index 9364638f..fe72d148 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__backdoor.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__backdoor.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__close_backend_fd.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__close_backend_fd.cpython-39.pyc
index ef446894..eda15ca9 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__close_backend_fd.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__close_backend_fd.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__compat.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__compat.cpython-39.pyc
index 30da4162..b7f6c9d6 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__compat.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__compat.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__contextvars.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__contextvars.cpython-39.pyc
index 116bd231..9054f49d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__contextvars.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__contextvars.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core.cpython-39.pyc
index 62a26562..9364331e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_async.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_async.cpython-39.pyc
index 2c63479f..46a5cd90 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_async.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_async.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_callback.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_callback.cpython-39.pyc
index fd08c174..0a8e2f3e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_callback.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_callback.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_fork.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_fork.cpython-39.pyc
index ff7c43e8..a3a1dafa 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_fork.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_fork.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_loop_run.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_loop_run.cpython-39.pyc
index 98400f4b..e2145b49 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_loop_run.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_loop_run.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_stat.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_stat.cpython-39.pyc
index c120b088..1b452e11 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_stat.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_stat.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_timer.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_timer.cpython-39.pyc
index 659d9df3..fba0253b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_timer.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_timer.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_watcher.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_watcher.cpython-39.pyc
index ec90797f..a7bf085b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_watcher.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__core_watcher.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__destroy.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__destroy.cpython-39.pyc
index b01ac20d..dbee73c3 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__destroy.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__destroy.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__destroy_default_loop.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__destroy_default_loop.cpython-39.pyc
index 42117ec1..76352018 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__destroy_default_loop.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__destroy_default_loop.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__doctests.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__doctests.cpython-39.pyc
index 32ec9579..448b1452 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__doctests.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__doctests.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__environ.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__environ.cpython-39.pyc
index f42d4e16..db425f8f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__environ.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__environ.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__event.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__event.cpython-39.pyc
index 1dafcba6..8fa973dc 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__event.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__event.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__events.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__events.cpython-39.pyc
index a3af375d..618cc9e2 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__events.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__events.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_echoserver.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_echoserver.cpython-39.pyc
index 092dab08..71e53e85 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_echoserver.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_echoserver.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_portforwarder.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_portforwarder.cpython-39.pyc
index fe15a866..6acd3c2e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_portforwarder.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_portforwarder.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_udp_client.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_udp_client.cpython-39.pyc
index 196e6319..969b7222 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_udp_client.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_udp_client.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_udp_server.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_udp_server.cpython-39.pyc
index 7434cf28..72f433c1 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_udp_server.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_udp_server.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_webproxy.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_webproxy.cpython-39.pyc
index e86c2cf6..62695e0e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_webproxy.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_webproxy.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_wsgiserver.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_wsgiserver.cpython-39.pyc
index 3db11749..c1c62a1c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_wsgiserver.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_wsgiserver.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_wsgiserver_ssl.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_wsgiserver_ssl.cpython-39.pyc
index 862238d4..26117580 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_wsgiserver_ssl.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__example_wsgiserver_ssl.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__examples.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__examples.cpython-39.pyc
index 3db82dfb..89eb7f1a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__examples.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__examples.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__exc_info.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__exc_info.cpython-39.pyc
index 81c480ba..3c8fa651 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__exc_info.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__exc_info.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__execmodules.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__execmodules.cpython-39.pyc
index 6d9f24fb..eeb1e886 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__execmodules.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__execmodules.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__fileobject.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__fileobject.cpython-39.pyc
index 63dd5edb..601ec768 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__fileobject.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__fileobject.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__getaddrinfo_import.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__getaddrinfo_import.cpython-39.pyc
index 54e254a8..dd3158e4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__getaddrinfo_import.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__getaddrinfo_import.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenio.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenio.cpython-39.pyc
index 2ffd9916..362f6d93 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenio.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenio.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenlet.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenlet.cpython-39.pyc
index ebc95c69..c6b5f2f5 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenlet.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenlet.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenletset.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenletset.cpython-39.pyc
index eb937b9c..8a94fbdf 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenletset.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenletset.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenness.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenness.cpython-39.pyc
index 911fb5a7..81a76dae 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenness.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__greenness.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__hub.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__hub.cpython-39.pyc
index 543e7d34..d3c2f467 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__hub.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__hub.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__hub_join.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__hub_join.cpython-39.pyc
index 9c2ea573..d39f3cb6 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__hub_join.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__hub_join.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__hub_join_timeout.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__hub_join_timeout.cpython-39.pyc
index 85beb51d..d73368c0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__hub_join_timeout.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__hub_join_timeout.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__import_blocking_in_greenlet.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__import_blocking_in_greenlet.cpython-39.pyc
index 9618e44e..3952ba24 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__import_blocking_in_greenlet.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__import_blocking_in_greenlet.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__import_wait.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__import_wait.cpython-39.pyc
index 5599c5b2..8fb280b8 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__import_wait.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__import_wait.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue112.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue112.cpython-39.pyc
index 0ce1ca55..9127508f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue112.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue112.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue1686.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue1686.cpython-39.pyc
index d55b57b2..aa7c4418 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue1686.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue1686.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue230.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue230.cpython-39.pyc
index 21687eea..0fcc1e54 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue230.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue230.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue330.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue330.cpython-39.pyc
index a16f925d..53c752ec 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue330.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue330.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue467.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue467.cpython-39.pyc
index 17cf403a..330699f4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue467.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue467.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue6.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue6.cpython-39.pyc
index 5353ce72..98b2af32 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue6.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue6.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue600.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue600.cpython-39.pyc
index 99f93977..8b08c8c2 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue600.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue600.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue607.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue607.cpython-39.pyc
index 027cf1f3..76314940 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue607.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue607.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue639.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue639.cpython-39.pyc
index 98523738..5acb667c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue639.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue639.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue_728.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue_728.cpython-39.pyc
index ce6a36bd..aa505083 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue_728.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issue_728.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issues461_471.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issues461_471.cpython-39.pyc
index bcd858e4..a0a52564 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issues461_471.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__issues461_471.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__iwait.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__iwait.cpython-39.pyc
index 4d55dc43..42857798 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__iwait.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__iwait.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__joinall.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__joinall.cpython-39.pyc
index de82892b..ba9455e2 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__joinall.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__joinall.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__local.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__local.cpython-39.pyc
index 261ae083..cf17f893 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__local.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__local.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__lock.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__lock.cpython-39.pyc
index 67e95a94..5028e612 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__lock.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__lock.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__loop_callback.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__loop_callback.cpython-39.pyc
index 9a2fd7a2..32f1bb6f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__loop_callback.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__loop_callback.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__makefile_ref.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__makefile_ref.cpython-39.pyc
index cebbfd3e..6bb6f196 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__makefile_ref.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__makefile_ref.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__memleak.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__memleak.cpython-39.pyc
index ef2e256f..9d73c736 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__memleak.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__memleak.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey.cpython-39.pyc
index 57de6236..7bc24010 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_builtins_future.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_builtins_future.cpython-39.pyc
index 8b87d6a9..81d975f7 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_builtins_future.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_builtins_future.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_futures_thread.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_futures_thread.cpython-39.pyc
index 73cbc848..761ef913 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_futures_thread.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_futures_thread.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_hub_in_thread.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_hub_in_thread.cpython-39.pyc
index f02d513f..54d2cb5b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_hub_in_thread.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_hub_in_thread.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_logging.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_logging.cpython-39.pyc
index 518c4462..3cf54bec 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_logging.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_logging.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_module_run.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_module_run.cpython-39.pyc
index fedae8b3..754b7335 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_module_run.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_module_run.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_multiple_imports.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_multiple_imports.cpython-39.pyc
index ce6d39c8..8e374e1e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_multiple_imports.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_multiple_imports.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_queue.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_queue.cpython-39.pyc
index 40b25d16..8f3c6ba3 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_queue.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_queue.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_select.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_select.cpython-39.pyc
index 1a9a602d..9d4c7c81 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_select.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_select.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_selectors.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_selectors.cpython-39.pyc
index 3af81047..798375b2 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_selectors.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_selectors.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_sigchld.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_sigchld.cpython-39.pyc
index 19709afd..b138ffcd 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_sigchld.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_sigchld.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_sigchld_2.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_sigchld_2.cpython-39.pyc
index b3b21bd8..2b05089f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_sigchld_2.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_sigchld_2.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_sigchld_3.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_sigchld_3.cpython-39.pyc
index e0bc8832..3e0e3de0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_sigchld_3.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_sigchld_3.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning.cpython-39.pyc
index 7479528e..97b0a6b0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning2.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning2.cpython-39.pyc
index 35c200be..49dad264 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning2.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning2.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning3.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning3.cpython-39.pyc
index 919b558f..e04f1d82 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning3.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__monkey_ssl_warning3.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__nondefaultloop.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__nondefaultloop.cpython-39.pyc
index 7503b850..3f2571e6 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__nondefaultloop.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__nondefaultloop.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__order.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__order.cpython-39.pyc
index 43b29b5d..08aca5ce 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__order.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__order.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__os.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__os.cpython-39.pyc
index 2ed305eb..15d4f0cd 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__os.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__os.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__pool.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__pool.cpython-39.pyc
index 7315f04e..7fb1b919 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__pool.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__pool.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__pywsgi.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__pywsgi.cpython-39.pyc
index f68acbb5..36183538 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__pywsgi.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__pywsgi.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__queue.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__queue.cpython-39.pyc
index c3ee31cf..f8fe7cad 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__queue.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__queue.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__real_greenlet.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__real_greenlet.cpython-39.pyc
index 60a9369d..97e06f32 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__real_greenlet.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__real_greenlet.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__refcount.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__refcount.cpython-39.pyc
index d298f300..88d1c25d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__refcount.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__refcount.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__refcount_core.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__refcount_core.cpython-39.pyc
index 450a34f2..186df1d1 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__refcount_core.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__refcount_core.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__resolver_dnspython.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__resolver_dnspython.cpython-39.pyc
index 88234df2..8c223b0d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__resolver_dnspython.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__resolver_dnspython.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__select.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__select.cpython-39.pyc
index 8e4ba124..03de066b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__select.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__select.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__selectors.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__selectors.cpython-39.pyc
index 36420751..9d682c3f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__selectors.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__selectors.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__semaphore.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__semaphore.cpython-39.pyc
index 60fc9615..d06c6583 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__semaphore.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__semaphore.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__server.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__server.cpython-39.pyc
index 6635ca8d..5bdb1e9f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__server.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__server.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__server_pywsgi.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__server_pywsgi.cpython-39.pyc
index b5fe6ac6..86d9fe35 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__server_pywsgi.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__server_pywsgi.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__signal.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__signal.cpython-39.pyc
index 5455fb03..a0c55822 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__signal.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__signal.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__sleep0.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__sleep0.cpython-39.pyc
index a20120f2..255cb906 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__sleep0.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__sleep0.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket.cpython-39.pyc
index 8e60e1d0..450787f2 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_close.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_close.cpython-39.pyc
index 11b202b0..29143701 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_close.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_close.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_dns.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_dns.cpython-39.pyc
index 8517dd39..9b58c3e6 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_dns.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_dns.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_dns6.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_dns6.cpython-39.pyc
index 29471cc0..b6a5339b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_dns6.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_dns6.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_errors.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_errors.cpython-39.pyc
index 77ae4d63..175139b2 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_errors.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_errors.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_ex.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_ex.cpython-39.pyc
index a5e57ff8..9c7b22ba 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_ex.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_ex.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_send_memoryview.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_send_memoryview.cpython-39.pyc
index 66c5d330..268e24a3 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_send_memoryview.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_send_memoryview.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_ssl.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_ssl.cpython-39.pyc
index dec2219f..d5847c96 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_ssl.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_ssl.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_timeout.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_timeout.cpython-39.pyc
index 6855028e..2f173518 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_timeout.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socket_timeout.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socketpair.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socketpair.cpython-39.pyc
index f0747d89..e286f5cd 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socketpair.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__socketpair.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__ssl.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__ssl.cpython-39.pyc
index 730a57f1..01c4caf6 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__ssl.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__ssl.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__subprocess.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__subprocess.cpython-39.pyc
index 164673e8..1e915e9a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__subprocess.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__subprocess.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__subprocess_interrupted.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__subprocess_interrupted.cpython-39.pyc
index b31c0dc4..3fece3c0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__subprocess_interrupted.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__subprocess_interrupted.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__subprocess_poll.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__subprocess_poll.cpython-39.pyc
index 170e7069..de784600 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__subprocess_poll.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__subprocess_poll.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__systemerror.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__systemerror.cpython-39.pyc
index f489e3d5..6e0d4403 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__systemerror.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__systemerror.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__thread.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__thread.cpython-39.pyc
index d2af73ae..728c78d2 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__thread.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__thread.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading.cpython-39.pyc
index ac8a2c07..d0686e19 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_2.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_2.cpython-39.pyc
index 285a7bb9..38ef82a7 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_2.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_2.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_before_monkey.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_before_monkey.cpython-39.pyc
index 79d2ee0a..03d0ae28 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_before_monkey.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_before_monkey.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_holding_lock_while_monkey.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_holding_lock_while_monkey.cpython-39.pyc
index 498ce798..b0ee947d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_holding_lock_while_monkey.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_holding_lock_while_monkey.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_monkey_in_thread.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_monkey_in_thread.cpython-39.pyc
index 25d6b26e..378a47a0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_monkey_in_thread.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_monkey_in_thread.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_native_before_monkey.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_native_before_monkey.cpython-39.pyc
index 20579539..67a701ca 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_native_before_monkey.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_native_before_monkey.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_no_monkey.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_no_monkey.cpython-39.pyc
index 6bb197e8..6f21769a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_no_monkey.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_no_monkey.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_patched_local.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_patched_local.cpython-39.pyc
index a80d312c..721fceb0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_patched_local.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_patched_local.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_vs_settrace.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_vs_settrace.cpython-39.pyc
index 1775dfe8..4304c188 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_vs_settrace.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threading_vs_settrace.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threadpool.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threadpool.cpython-39.pyc
index 1cfce1bd..964d409e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threadpool.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threadpool.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threadpool_executor_patched.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threadpool_executor_patched.cpython-39.pyc
index 53e4b81d..dab20b52 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threadpool_executor_patched.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__threadpool_executor_patched.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__timeout.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__timeout.cpython-39.pyc
index 7ffcc4fd..db0922f0 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__timeout.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__timeout.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__util.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__util.cpython-39.pyc
index 18c4f614..bece02a4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__util.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/__pycache__/test__util.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/__init__.cpython-39.pyc
index 419feff3..08b4768e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/__main__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/__main__.cpython-39.pyc
index e0622a8d..6fe29331 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/__main__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/__main__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/issue1526_no_monkey.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/issue1526_no_monkey.cpython-39.pyc
index 8db5518c..362e92f1 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/issue1526_no_monkey.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/issue1526_no_monkey.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/issue1526_with_monkey.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/issue1526_with_monkey.cpython-39.pyc
index 8e048e41..b24b8a07 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/issue1526_with_monkey.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/issue1526_with_monkey.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/issue302monkey.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/issue302monkey.cpython-39.pyc
index 4470f54b..9bb90413 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/issue302monkey.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/issue302monkey.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/script.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/script.cpython-39.pyc
index 830ed110..3b3fe955 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/script.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/script.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/threadpool_monkey_patches.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/threadpool_monkey_patches.cpython-39.pyc
index 85dd5382..e95040a2 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/threadpool_monkey_patches.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/threadpool_monkey_patches.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/threadpool_no_monkey.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/threadpool_no_monkey.cpython-39.pyc
index be073643..4fb5eccb 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/threadpool_no_monkey.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/gevent/tests/monkey_package/__pycache__/threadpool_no_monkey.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/__init__.cpython-39.pyc
index eb212fcc..c566444c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/_compat.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/_compat.cpython-39.pyc
index eb76cd26..f520a707 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/_compat.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/_compat.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/exceptions.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/exceptions.cpython-39.pyc
index 684037f0..7cd4d1d2 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/exceptions.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/exceptions.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/handler.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/handler.cpython-39.pyc
index dd984926..c28c2edd 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/handler.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/handler.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/logging.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/logging.cpython-39.pyc
index a5948b69..24f22706 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/logging.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/logging.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/resource.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/resource.cpython-39.pyc
index 03bbc92c..937af1f4 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/resource.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/resource.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/server.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/server.cpython-39.pyc
index b80e7bb3..4bdc7f74 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/server.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/server.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/utf8validator.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/utf8validator.cpython-39.pyc
index 4a0ecb98..4b974706 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/utf8validator.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/utf8validator.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/utils.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/utils.cpython-39.pyc
index 7f758449..5e24977f 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/utils.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/utils.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/websocket.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/websocket.cpython-39.pyc
index 81e6341c..7ce62e6c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/websocket.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/__pycache__/websocket.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/gunicorn/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/gunicorn/__pycache__/__init__.cpython-39.pyc
index de97d5f5..96ca6d70 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/gunicorn/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/gunicorn/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/gunicorn/__pycache__/workers.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/gunicorn/__pycache__/workers.cpython-39.pyc
index bac6611c..12ed7373 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/gunicorn/__pycache__/workers.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/gunicorn/__pycache__/workers.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/protocols/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/protocols/__pycache__/__init__.cpython-39.pyc
index 109d77ec..80ae1445 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/protocols/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/protocols/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/protocols/__pycache__/base.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/protocols/__pycache__/base.cpython-39.pyc
index be3e0da3..cc6f141d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/protocols/__pycache__/base.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/protocols/__pycache__/base.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/protocols/__pycache__/wamp.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/protocols/__pycache__/wamp.cpython-39.pyc
index 99b759fc..1dedf3ad 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/protocols/__pycache__/wamp.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/geventwebsocket/protocols/__pycache__/wamp.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/__pycache__/__init__.cpython-39.pyc
index 25661c70..9907d1bf 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/__init__.cpython-39.pyc
index 7966ae90..7f0320ae 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/__init__.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/__init__.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_contextvars.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_contextvars.cpython-39.pyc
index 7d669b7f..cccbaf38 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_contextvars.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_contextvars.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_cpp.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_cpp.cpython-39.pyc
index 7266c392..10a5157a 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_cpp.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_cpp.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_extension_interface.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_extension_interface.cpython-39.pyc
index 456ab844..17ad6f2b 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_extension_interface.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_extension_interface.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_gc.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_gc.cpython-39.pyc
index f921fab1..935ec142 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_gc.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_gc.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_generator.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_generator.cpython-39.pyc
index e1795692..b301f12d 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_generator.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_generator.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_generator_nested.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_generator_nested.cpython-39.pyc
index bb6b38d9..1a03ceed 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_generator_nested.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_generator_nested.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_greenlet.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_greenlet.cpython-39.pyc
index ece919be..47c3d673 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_greenlet.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_greenlet.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_leaks.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_leaks.cpython-39.pyc
index b75667ad..b0af1b4e 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_leaks.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_leaks.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_stack_saved.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_stack_saved.cpython-39.pyc
index 88a07e24..132db186 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_stack_saved.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_stack_saved.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_throw.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_throw.cpython-39.pyc
index 74c3fae4..ed2f655c 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_throw.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_throw.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_tracing.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_tracing.cpython-39.pyc
index 0f5fe0a8..1bcd59bd 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_tracing.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_tracing.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_version.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_version.cpython-39.pyc
index c9726680..df7ffc90 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_version.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_version.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_weakref.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_weakref.cpython-39.pyc
index a01f7b8d..3675fdfb 100644
Binary files a/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_weakref.cpython-39.pyc and b/IKEA_scraper/.venv/Lib/site-packages/greenlet/tests/__pycache__/test_weakref.cpython-39.pyc differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/INSTALLER b/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/INSTALLER
deleted file mode 100644
index a1b589e3..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/LICENSE.txt b/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/LICENSE.txt
deleted file mode 100644
index 8f080eae..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/LICENSE.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2016 Nathaniel J. Smith and other contributors
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/METADATA b/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/METADATA
deleted file mode 100644
index 5478c3cc..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/METADATA
+++ /dev/null
@@ -1,194 +0,0 @@
-Metadata-Version: 2.1
-Name: h11
-Version: 0.12.0
-Summary: A pure-Python, bring-your-own-I/O implementation of HTTP/1.1
-Home-page: https://github.com/python-hyper/h11
-Author: Nathaniel J. Smith
-Author-email: njs@pobox.com
-License: MIT
-Platform: UNKNOWN
-Classifier: Development Status :: 3 - Alpha
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Topic :: Internet :: WWW/HTTP
-Classifier: Topic :: System :: Networking
-Requires-Python: >=3.6
-
-h11
-===
-
-.. image:: https://travis-ci.org/python-hyper/h11.svg?branch=master
- :target: https://travis-ci.org/python-hyper/h11
- :alt: Automated test status
-
-.. image:: https://codecov.io/gh/python-hyper/h11/branch/master/graph/badge.svg
- :target: https://codecov.io/gh/python-hyper/h11
- :alt: Test coverage
-
-.. image:: https://readthedocs.org/projects/h11/badge/?version=latest
- :target: http://h11.readthedocs.io/en/latest/?badge=latest
- :alt: Documentation Status
-
-This is a little HTTP/1.1 library written from scratch in Python,
-heavily inspired by `hyper-h2 `_.
-
-It's a "bring-your-own-I/O" library; h11 contains no IO code
-whatsoever. This means you can hook h11 up to your favorite network
-API, and that could be anything you want: synchronous, threaded,
-asynchronous, or your own implementation of `RFC 6214
-`_ -- h11 won't judge you.
-(Compare this to the current state of the art, where every time a `new
-network API `_ comes along then someone
-gets to start over reimplementing the entire HTTP protocol from
-scratch.) Cory Benfield made an `excellent blog post describing the
-benefits of this approach
-`_, or if you like video
-then here's his `PyCon 2016 talk on the same theme
-`_.
-
-This also means that h11 is not immediately useful out of the box:
-it's a toolkit for building programs that speak HTTP, not something
-that could directly replace ``requests`` or ``twisted.web`` or
-whatever. But h11 makes it much easier to implement something like
-``requests`` or ``twisted.web``.
-
-At a high level, working with h11 goes like this:
-
-1) First, create an ``h11.Connection`` object to track the state of a
- single HTTP/1.1 connection.
-
-2) When you read data off the network, pass it to
- ``conn.receive_data(...)``; you'll get back a list of objects
- representing high-level HTTP "events".
-
-3) When you want to send a high-level HTTP event, create the
- corresponding "event" object and pass it to ``conn.send(...)``;
- this will give you back some bytes that you can then push out
- through the network.
-
-For example, a client might instantiate and then send a
-``h11.Request`` object, then zero or more ``h11.Data`` objects for the
-request body (e.g., if this is a POST), and then a
-``h11.EndOfMessage`` to indicate the end of the message. Then the
-server would then send back a ``h11.Response``, some ``h11.Data``, and
-its own ``h11.EndOfMessage``. If either side violates the protocol,
-you'll get a ``h11.ProtocolError`` exception.
-
-h11 is suitable for implementing both servers and clients, and has a
-pleasantly symmetric API: the events you send as a client are exactly
-the ones that you receive as a server and vice-versa.
-
-`Here's an example of a tiny HTTP client
-`_
-
-It also has `a fine manual `_.
-
-FAQ
----
-
-*Whyyyyy?*
-
-I wanted to play with HTTP in `Curio
-`__ and `Trio
-`__, which at the time didn't have any
-HTTP libraries. So I thought, no big deal, Python has, like, a dozen
-different implementations of HTTP, surely I can find one that's
-reusable. I didn't find one, but I did find Cory's call-to-arms
-blog-post. So I figured, well, fine, if I have to implement HTTP from
-scratch, at least I can make sure no-one *else* has to ever again.
-
-*Should I use it?*
-
-Maybe. You should be aware that it's a very young project. But, it's
-feature complete and has an exhaustive test-suite and complete docs,
-so the next step is for people to try using it and see how it goes
-:-). If you do then please let us know -- if nothing else we'll want
-to talk to you before making any incompatible changes!
-
-*What are the features/limitations?*
-
-Roughly speaking, it's trying to be a robust, complete, and non-hacky
-implementation of the first "chapter" of the HTTP/1.1 spec: `RFC 7230:
-HTTP/1.1 Message Syntax and Routing
-`_. That is, it mostly focuses on
-implementing HTTP at the level of taking bytes on and off the wire,
-and the headers related to that, and tries to be anal about spec
-conformance. It doesn't know about higher-level concerns like URL
-routing, conditional GETs, cross-origin cookie policies, or content
-negotiation. But it does know how to take care of framing,
-cross-version differences in keep-alive handling, and the "obsolete
-line folding" rule, so you can focus your energies on the hard /
-interesting parts for your application, and it tries to support the
-full specification in the sense that any useful HTTP/1.1 conformant
-application should be able to use h11.
-
-It's pure Python, and has no dependencies outside of the standard
-library.
-
-It has a test suite with 100.0% coverage for both statements and
-branches.
-
-Currently it supports Python 3 (testing on 3.6-3.9) and PyPy 3.
-The last Python 2-compatible version was h11 0.11.x.
-(Originally it had a Cython wrapper for `http-parser
-`_ and a beautiful nested state
-machine implemented with ``yield from`` to postprocess the output. But
-I had to take these out -- the new *parser* needs fewer lines-of-code
-than the old *parser wrapper*, is written in pure Python, uses no
-exotic language syntax, and has more features. It's sad, really; that
-old state machine was really slick. I just need a few sentences here
-to mourn that.)
-
-I don't know how fast it is. I haven't benchmarked or profiled it yet,
-so it's probably got a few pointless hot spots, and I've been trying
-to err on the side of simplicity and robustness instead of
-micro-optimization. But at the architectural level I tried hard to
-avoid fundamentally bad decisions, e.g., I believe that all the
-parsing algorithms remain linear-time even in the face of pathological
-input like slowloris, and there are no byte-by-byte loops. (I also
-believe that it maintains bounded memory usage in the face of
-arbitrary/pathological input.)
-
-The whole library is ~800 lines-of-code. You can read and understand
-the whole thing in less than an hour. Most of the energy invested in
-this so far has been spent on trying to keep things simple by
-minimizing special-cases and ad hoc state manipulation; even though it
-is now quite small and simple, I'm still annoyed that I haven't
-figured out how to make it even smaller and simpler. (Unfortunately,
-HTTP does not lend itself to simplicity.)
-
-The API is ~feature complete and I don't expect the general outlines
-to change much, but you can't judge an API's ergonomics until you
-actually document and use it, so I'd expect some changes in the
-details.
-
-*How do I try it?*
-
-.. code-block:: sh
-
- $ pip install h11
- $ git clone git@github.com:python-hyper/h11
- $ cd h11/examples
- $ python basic-client.py
-
-and go from there.
-
-*License?*
-
-MIT
-
-*Code of conduct?*
-
-Contributors are requested to follow our `code of conduct
-`_ in
-all project spaces.
-
-
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/RECORD b/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/RECORD
deleted file mode 100644
index aa6f50d2..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/RECORD
+++ /dev/null
@@ -1,51 +0,0 @@
-h11-0.12.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-h11-0.12.0.dist-info/LICENSE.txt,sha256=N9tbuFkm2yikJ6JYZ_ELEjIAOuob5pzLhRE4rbjm82E,1124
-h11-0.12.0.dist-info/METADATA,sha256=_X-4TWqWCxSJ_mDyAbZPzdxHqP290_yVu09nelJOk04,8109
-h11-0.12.0.dist-info/RECORD,,
-h11-0.12.0.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
-h11-0.12.0.dist-info/top_level.txt,sha256=F7dC4jl3zeh8TGHEPaWJrMbeuoWbS379Gwdi-Yvdcis,4
-h11/__init__.py,sha256=3gYpvQiX8_6-dyXaAxQt_sIYREVTz1T-zB5Lf4hjKt0,909
-h11/__pycache__/__init__.cpython-39.pyc,,
-h11/__pycache__/_abnf.cpython-39.pyc,,
-h11/__pycache__/_connection.cpython-39.pyc,,
-h11/__pycache__/_events.cpython-39.pyc,,
-h11/__pycache__/_headers.cpython-39.pyc,,
-h11/__pycache__/_readers.cpython-39.pyc,,
-h11/__pycache__/_receivebuffer.cpython-39.pyc,,
-h11/__pycache__/_state.cpython-39.pyc,,
-h11/__pycache__/_util.cpython-39.pyc,,
-h11/__pycache__/_version.cpython-39.pyc,,
-h11/__pycache__/_writers.cpython-39.pyc,,
-h11/_abnf.py,sha256=tMKqgOEkTHHp8sPd_gmU9Qowe_yXXrihct63RX2zJsg,4637
-h11/_connection.py,sha256=XFZ-LPb3C2vgF4v5ifmcJqX-a2tHkItucJ7uIGvPYZA,24964
-h11/_events.py,sha256=IJtM7i2TxKv0S-givq2b-oehPVsmsbsIelTW6NHcIvg,9834
-h11/_headers.py,sha256=P2h8Q39SIFiRS9CpYjAwo_99XKJUvLHjn0U3tnm4qHE,9130
-h11/_readers.py,sha256=DmJKQwH9Iu7U3WNljKB09d6iJIO6P2_WtylJEY3HvPY,7280
-h11/_receivebuffer.py,sha256=pMOLWjS53haaCm73O6tSWKFD_6BQQWzVLqLCm2ouvcE,5029
-h11/_state.py,sha256=Upg0_uiO_C_QNXHxLB4YUprEeoeso0i_ma12SOrrA54,12167
-h11/_util.py,sha256=Lw_CoIUMR8wjnvgKwo94FCdmFcIbRQsokmxpBV7LcTI,4387
-h11/_version.py,sha256=14wRZqPo0n2t5kFKCQLsldnyZAfOZoKPJbbwJnbGPcc,686
-h11/_writers.py,sha256=dj8HQ4Pnzq5SjkUZrgh3RKQ6-8Ecy9RKC1MjSo27y4s,4173
-h11/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-h11/tests/__pycache__/__init__.cpython-39.pyc,,
-h11/tests/__pycache__/helpers.cpython-39.pyc,,
-h11/tests/__pycache__/test_against_stdlib_http.cpython-39.pyc,,
-h11/tests/__pycache__/test_connection.cpython-39.pyc,,
-h11/tests/__pycache__/test_events.cpython-39.pyc,,
-h11/tests/__pycache__/test_headers.cpython-39.pyc,,
-h11/tests/__pycache__/test_helpers.cpython-39.pyc,,
-h11/tests/__pycache__/test_io.cpython-39.pyc,,
-h11/tests/__pycache__/test_receivebuffer.cpython-39.pyc,,
-h11/tests/__pycache__/test_state.cpython-39.pyc,,
-h11/tests/__pycache__/test_util.cpython-39.pyc,,
-h11/tests/data/test-file,sha256=ZJ03Rqs98oJw29OHzJg7LlMzyGQaRAY0r3AqBeM2wVU,65
-h11/tests/helpers.py,sha256=nKheRzldPf278C81d_9_Mb9yWsYJ5udwKg_oq-fAz-U,2528
-h11/tests/test_against_stdlib_http.py,sha256=aA4oDd3_jXkapvW0ER9dbGxIiNt6Ytsfs3U2Rd5XtUc,3700
-h11/tests/test_connection.py,sha256=1WybI9IQROZ0QPtR2wQjetPIR_Jwsvw5i5j2fO7XtcI,36375
-h11/tests/test_events.py,sha256=RTPFBIg81Muc7ZoDhsLwaZxthD76R1UCzHF5nzsbM-Q,5182
-h11/tests/test_headers.py,sha256=pa-WMjCk8ZXJFABkojr2db7ZKrgNKiwl-D-hjjt6-Eg,5390
-h11/tests/test_helpers.py,sha256=mPOAiv4HtyG0_T23K_ihh1JUs0y71ykD47c9r3iVtz0,573
-h11/tests/test_io.py,sha256=oaIEAy3ktA_e1xuyP09fX_GiSlS7GKMlFhQIdkg-EhI,15494
-h11/tests/test_receivebuffer.py,sha256=nZ9_LXj3wfyOn4dkgvjnDjZeNTEtxO8-lNphAB0FVF0,3399
-h11/tests/test_state.py,sha256=JMKqA2d2wtskf7FbsAr1s9qsIul4WtwdXVAOCUJgalk,8551
-h11/tests/test_util.py,sha256=j28tMloUSuhlpUxmgvS1PRurRFSbyzWb7yCTp6qy9_Q,2710
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/WHEEL b/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/WHEEL
deleted file mode 100644
index 385faab0..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/WHEEL
+++ /dev/null
@@ -1,5 +0,0 @@
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.36.2)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/top_level.txt b/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/top_level.txt
deleted file mode 100644
index 0d24def7..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11-0.12.0.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-h11
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/__init__.py b/IKEA_scraper/.venv/Lib/site-packages/h11/__init__.py
deleted file mode 100644
index ae39e012..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/__init__.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# A highish-level implementation of the HTTP/1.1 wire protocol (RFC 7230),
-# containing no networking code at all, loosely modelled on hyper-h2's generic
-# implementation of HTTP/2 (and in particular the h2.connection.H2Connection
-# class). There's still a bunch of subtle details you need to get right if you
-# want to make this actually useful, because it doesn't implement all the
-# semantics to check that what you're asking to write to the wire is sensible,
-# but at least it gets you out of dealing with the wire itself.
-
-from ._connection import *
-from ._events import *
-from ._state import *
-from ._util import LocalProtocolError, ProtocolError, RemoteProtocolError
-from ._version import __version__
-
-PRODUCT_ID = "python-h11/" + __version__
-
-
-__all__ = ["ProtocolError", "LocalProtocolError", "RemoteProtocolError"]
-__all__ += _events.__all__
-__all__ += _connection.__all__
-__all__ += _state.__all__
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/__init__.cpython-39.pyc
deleted file mode 100644
index 84f9f822..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/__init__.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_abnf.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_abnf.cpython-39.pyc
deleted file mode 100644
index 988037ba..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_abnf.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_connection.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_connection.cpython-39.pyc
deleted file mode 100644
index f31278d6..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_connection.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_events.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_events.cpython-39.pyc
deleted file mode 100644
index 113bdd6d..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_events.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_headers.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_headers.cpython-39.pyc
deleted file mode 100644
index c062a092..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_headers.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_readers.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_readers.cpython-39.pyc
deleted file mode 100644
index 78dab266..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_readers.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_receivebuffer.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_receivebuffer.cpython-39.pyc
deleted file mode 100644
index 69f4bf6c..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_receivebuffer.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_state.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_state.cpython-39.pyc
deleted file mode 100644
index e3777acd..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_state.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_util.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_util.cpython-39.pyc
deleted file mode 100644
index 3443e822..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_util.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_version.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_version.cpython-39.pyc
deleted file mode 100644
index 30bf3d95..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_version.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_writers.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_writers.cpython-39.pyc
deleted file mode 100644
index a539149c..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/__pycache__/_writers.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/_abnf.py b/IKEA_scraper/.venv/Lib/site-packages/h11/_abnf.py
deleted file mode 100644
index e6d49e1e..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/_abnf.py
+++ /dev/null
@@ -1,129 +0,0 @@
-# We use native strings for all the re patterns, to take advantage of string
-# formatting, and then convert to bytestrings when compiling the final re
-# objects.
-
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#whitespace
-# OWS = *( SP / HTAB )
-# ; optional whitespace
-OWS = r"[ \t]*"
-
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.token.separators
-# token = 1*tchar
-#
-# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
-# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
-# / DIGIT / ALPHA
-# ; any VCHAR, except delimiters
-token = r"[-!#$%&'*+.^_`|~0-9a-zA-Z]+"
-
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#header.fields
-# field-name = token
-field_name = token
-
-# The standard says:
-#
-# field-value = *( field-content / obs-fold )
-# field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
-# field-vchar = VCHAR / obs-text
-# obs-fold = CRLF 1*( SP / HTAB )
-# ; obsolete line folding
-# ; see Section 3.2.4
-#
-# https://tools.ietf.org/html/rfc5234#appendix-B.1
-#
-# VCHAR = %x21-7E
-# ; visible (printing) characters
-#
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.quoted-string
-# obs-text = %x80-FF
-#
-# However, the standard definition of field-content is WRONG! It disallows
-# fields containing a single visible character surrounded by whitespace,
-# e.g. "foo a bar".
-#
-# See: https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189
-#
-# So our definition of field_content attempts to fix it up...
-#
-# Also, we allow lots of control characters, because apparently people assume
-# that they're legal in practice (e.g., google analytics makes cookies with
-# \x01 in them!):
-# https://github.com/python-hyper/h11/issues/57
-# We still don't allow NUL or whitespace, because those are often treated as
-# meta-characters and letting them through can lead to nasty issues like SSRF.
-vchar = r"[\x21-\x7e]"
-vchar_or_obs_text = r"[^\x00\s]"
-field_vchar = vchar_or_obs_text
-field_content = r"{field_vchar}+(?:[ \t]+{field_vchar}+)*".format(**globals())
-
-# We handle obs-fold at a different level, and our fixed-up field_content
-# already grows to swallow the whole value, so ? instead of *
-field_value = r"({field_content})?".format(**globals())
-
-# header-field = field-name ":" OWS field-value OWS
-header_field = (
- r"(?P{field_name})"
- r":"
- r"{OWS}"
- r"(?P{field_value})"
- r"{OWS}".format(**globals())
-)
-
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#request.line
-#
-# request-line = method SP request-target SP HTTP-version CRLF
-# method = token
-# HTTP-version = HTTP-name "/" DIGIT "." DIGIT
-# HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive
-#
-# request-target is complicated (see RFC 7230 sec 5.3) -- could be path, full
-# URL, host+port (for connect), or even "*", but in any case we are guaranteed
-# that it contists of the visible printing characters.
-method = token
-request_target = r"{vchar}+".format(**globals())
-http_version = r"HTTP/(?P[0-9]\.[0-9])"
-request_line = (
- r"(?P{method})"
- r" "
- r"(?P{request_target})"
- r" "
- r"{http_version}".format(**globals())
-)
-
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#status.line
-#
-# status-line = HTTP-version SP status-code SP reason-phrase CRLF
-# status-code = 3DIGIT
-# reason-phrase = *( HTAB / SP / VCHAR / obs-text )
-status_code = r"[0-9]{3}"
-reason_phrase = r"([ \t]|{vchar_or_obs_text})*".format(**globals())
-status_line = (
- r"{http_version}"
- r" "
- r"(?P{status_code})"
- # However, there are apparently a few too many servers out there that just
- # leave out the reason phrase:
- # https://github.com/scrapy/scrapy/issues/345#issuecomment-281756036
- # https://github.com/seanmonstar/httparse/issues/29
- # so make it optional. ?: is a non-capturing group.
- r"(?: (?P{reason_phrase}))?".format(**globals())
-)
-
-HEXDIG = r"[0-9A-Fa-f]"
-# Actually
-#
-# chunk-size = 1*HEXDIG
-#
-# but we impose an upper-limit to avoid ridiculosity. len(str(2**64)) == 20
-chunk_size = r"({HEXDIG}){{1,20}}".format(**globals())
-# Actually
-#
-# chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
-#
-# but we aren't parsing the things so we don't really care.
-chunk_ext = r";.*"
-chunk_header = (
- r"(?P{chunk_size})"
- r"(?P{chunk_ext})?"
- r"\r\n".format(**globals())
-)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/_connection.py b/IKEA_scraper/.venv/Lib/site-packages/h11/_connection.py
deleted file mode 100644
index 6f796ef5..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/_connection.py
+++ /dev/null
@@ -1,585 +0,0 @@
-# This contains the main Connection class. Everything in h11 revolves around
-# this.
-
-from ._events import * # Import all event types
-from ._headers import get_comma_header, has_expect_100_continue, set_comma_header
-from ._readers import READERS
-from ._receivebuffer import ReceiveBuffer
-from ._state import * # Import all state sentinels
-from ._state import _SWITCH_CONNECT, _SWITCH_UPGRADE, ConnectionState
-from ._util import ( # Import the internal things we need
- LocalProtocolError,
- make_sentinel,
- RemoteProtocolError,
-)
-from ._writers import WRITERS
-
-# Everything in __all__ gets re-exported as part of the h11 public API.
-__all__ = ["Connection", "NEED_DATA", "PAUSED"]
-
-NEED_DATA = make_sentinel("NEED_DATA")
-PAUSED = make_sentinel("PAUSED")
-
-# If we ever have this much buffered without it making a complete parseable
-# event, we error out. The only time we really buffer is when reading the
-# request/reponse line + headers together, so this is effectively the limit on
-# the size of that.
-#
-# Some precedents for defaults:
-# - node.js: 80 * 1024
-# - tomcat: 8 * 1024
-# - IIS: 16 * 1024
-# - Apache: <8 KiB per line>
-DEFAULT_MAX_INCOMPLETE_EVENT_SIZE = 16 * 1024
-
-# RFC 7230's rules for connection lifecycles:
-# - If either side says they want to close the connection, then the connection
-# must close.
-# - HTTP/1.1 defaults to keep-alive unless someone says Connection: close
-# - HTTP/1.0 defaults to close unless both sides say Connection: keep-alive
-# (and even this is a mess -- e.g. if you're implementing a proxy then
-# sending Connection: keep-alive is forbidden).
-#
-# We simplify life by simply not supporting keep-alive with HTTP/1.0 peers. So
-# our rule is:
-# - If someone says Connection: close, we will close
-# - If someone uses HTTP/1.0, we will close.
-def _keep_alive(event):
- connection = get_comma_header(event.headers, b"connection")
- if b"close" in connection:
- return False
- if getattr(event, "http_version", b"1.1") < b"1.1":
- return False
- return True
-
-
-def _body_framing(request_method, event):
- # Called when we enter SEND_BODY to figure out framing information for
- # this body.
- #
- # These are the only two events that can trigger a SEND_BODY state:
- assert type(event) in (Request, Response)
- # Returns one of:
- #
- # ("content-length", count)
- # ("chunked", ())
- # ("http/1.0", ())
- #
- # which are (lookup key, *args) for constructing body reader/writer
- # objects.
- #
- # Reference: https://tools.ietf.org/html/rfc7230#section-3.3.3
- #
- # Step 1: some responses always have an empty body, regardless of what the
- # headers say.
- if type(event) is Response:
- if (
- event.status_code in (204, 304)
- or request_method == b"HEAD"
- or (request_method == b"CONNECT" and 200 <= event.status_code < 300)
- ):
- return ("content-length", (0,))
- # Section 3.3.3 also lists another case -- responses with status_code
- # < 200. For us these are InformationalResponses, not Responses, so
- # they can't get into this function in the first place.
- assert event.status_code >= 200
-
- # Step 2: check for Transfer-Encoding (T-E beats C-L):
- transfer_encodings = get_comma_header(event.headers, b"transfer-encoding")
- if transfer_encodings:
- assert transfer_encodings == [b"chunked"]
- return ("chunked", ())
-
- # Step 3: check for Content-Length
- content_lengths = get_comma_header(event.headers, b"content-length")
- if content_lengths:
- return ("content-length", (int(content_lengths[0]),))
-
- # Step 4: no applicable headers; fallback/default depends on type
- if type(event) is Request:
- return ("content-length", (0,))
- else:
- return ("http/1.0", ())
-
-
-################################################################
-#
-# The main Connection class
-#
-################################################################
-
-
-class Connection:
- """An object encapsulating the state of an HTTP connection.
-
- Args:
- our_role: If you're implementing a client, pass :data:`h11.CLIENT`. If
- you're implementing a server, pass :data:`h11.SERVER`.
-
- max_incomplete_event_size (int):
- The maximum number of bytes we're willing to buffer of an
- incomplete event. In practice this mostly sets a limit on the
- maximum size of the request/response line + headers. If this is
- exceeded, then :meth:`next_event` will raise
- :exc:`RemoteProtocolError`.
-
- """
-
- def __init__(
- self, our_role, max_incomplete_event_size=DEFAULT_MAX_INCOMPLETE_EVENT_SIZE
- ):
- self._max_incomplete_event_size = max_incomplete_event_size
- # State and role tracking
- if our_role not in (CLIENT, SERVER):
- raise ValueError("expected CLIENT or SERVER, not {!r}".format(our_role))
- self.our_role = our_role
- if our_role is CLIENT:
- self.their_role = SERVER
- else:
- self.their_role = CLIENT
- self._cstate = ConnectionState()
-
- # Callables for converting data->events or vice-versa given the
- # current state
- self._writer = self._get_io_object(self.our_role, None, WRITERS)
- self._reader = self._get_io_object(self.their_role, None, READERS)
-
- # Holds any unprocessed received data
- self._receive_buffer = ReceiveBuffer()
- # If this is true, then it indicates that the incoming connection was
- # closed *after* the end of whatever's in self._receive_buffer:
- self._receive_buffer_closed = False
-
- # Extra bits of state that don't fit into the state machine.
- #
- # These two are only used to interpret framing headers for figuring
- # out how to read/write response bodies. their_http_version is also
- # made available as a convenient public API.
- self.their_http_version = None
- self._request_method = None
- # This is pure flow-control and doesn't at all affect the set of legal
- # transitions, so no need to bother ConnectionState with it:
- self.client_is_waiting_for_100_continue = False
-
- @property
- def states(self):
- """A dictionary like::
-
- {CLIENT: , SERVER: }
-
- See :ref:`state-machine` for details.
-
- """
- return dict(self._cstate.states)
-
- @property
- def our_state(self):
- """The current state of whichever role we are playing. See
- :ref:`state-machine` for details.
- """
- return self._cstate.states[self.our_role]
-
- @property
- def their_state(self):
- """The current state of whichever role we are NOT playing. See
- :ref:`state-machine` for details.
- """
- return self._cstate.states[self.their_role]
-
- @property
- def they_are_waiting_for_100_continue(self):
- return self.their_role is CLIENT and self.client_is_waiting_for_100_continue
-
- def start_next_cycle(self):
- """Attempt to reset our connection state for a new request/response
- cycle.
-
- If both client and server are in :data:`DONE` state, then resets them
- both to :data:`IDLE` state in preparation for a new request/response
- cycle on this same connection. Otherwise, raises a
- :exc:`LocalProtocolError`.
-
- See :ref:`keepalive-and-pipelining`.
-
- """
- old_states = dict(self._cstate.states)
- self._cstate.start_next_cycle()
- self._request_method = None
- # self.their_http_version gets left alone, since it presumably lasts
- # beyond a single request/response cycle
- assert not self.client_is_waiting_for_100_continue
- self._respond_to_state_changes(old_states)
-
- def _process_error(self, role):
- old_states = dict(self._cstate.states)
- self._cstate.process_error(role)
- self._respond_to_state_changes(old_states)
-
- def _server_switch_event(self, event):
- if type(event) is InformationalResponse and event.status_code == 101:
- return _SWITCH_UPGRADE
- if type(event) is Response:
- if (
- _SWITCH_CONNECT in self._cstate.pending_switch_proposals
- and 200 <= event.status_code < 300
- ):
- return _SWITCH_CONNECT
- return None
-
- # All events go through here
- def _process_event(self, role, event):
- # First, pass the event through the state machine to make sure it
- # succeeds.
- old_states = dict(self._cstate.states)
- if role is CLIENT and type(event) is Request:
- if event.method == b"CONNECT":
- self._cstate.process_client_switch_proposal(_SWITCH_CONNECT)
- if get_comma_header(event.headers, b"upgrade"):
- self._cstate.process_client_switch_proposal(_SWITCH_UPGRADE)
- server_switch_event = None
- if role is SERVER:
- server_switch_event = self._server_switch_event(event)
- self._cstate.process_event(role, type(event), server_switch_event)
-
- # Then perform the updates triggered by it.
-
- # self._request_method
- if type(event) is Request:
- self._request_method = event.method
-
- # self.their_http_version
- if role is self.their_role and type(event) in (
- Request,
- Response,
- InformationalResponse,
- ):
- self.their_http_version = event.http_version
-
- # Keep alive handling
- #
- # RFC 7230 doesn't really say what one should do if Connection: close
- # shows up on a 1xx InformationalResponse. I think the idea is that
- # this is not supposed to happen. In any case, if it does happen, we
- # ignore it.
- if type(event) in (Request, Response) and not _keep_alive(event):
- self._cstate.process_keep_alive_disabled()
-
- # 100-continue
- if type(event) is Request and has_expect_100_continue(event):
- self.client_is_waiting_for_100_continue = True
- if type(event) in (InformationalResponse, Response):
- self.client_is_waiting_for_100_continue = False
- if role is CLIENT and type(event) in (Data, EndOfMessage):
- self.client_is_waiting_for_100_continue = False
-
- self._respond_to_state_changes(old_states, event)
-
- def _get_io_object(self, role, event, io_dict):
- # event may be None; it's only used when entering SEND_BODY
- state = self._cstate.states[role]
- if state is SEND_BODY:
- # Special case: the io_dict has a dict of reader/writer factories
- # that depend on the request/response framing.
- framing_type, args = _body_framing(self._request_method, event)
- return io_dict[SEND_BODY][framing_type](*args)
- else:
- # General case: the io_dict just has the appropriate reader/writer
- # for this state
- return io_dict.get((role, state))
-
- # This must be called after any action that might have caused
- # self._cstate.states to change.
- def _respond_to_state_changes(self, old_states, event=None):
- # Update reader/writer
- if self.our_state != old_states[self.our_role]:
- self._writer = self._get_io_object(self.our_role, event, WRITERS)
- if self.their_state != old_states[self.their_role]:
- self._reader = self._get_io_object(self.their_role, event, READERS)
-
- @property
- def trailing_data(self):
- """Data that has been received, but not yet processed, represented as
- a tuple with two elements, where the first is a byte-string containing
- the unprocessed data itself, and the second is a bool that is True if
- the receive connection was closed.
-
- See :ref:`switching-protocols` for discussion of why you'd want this.
- """
- return (bytes(self._receive_buffer), self._receive_buffer_closed)
-
- def receive_data(self, data):
- """Add data to our internal receive buffer.
-
- This does not actually do any processing on the data, just stores
- it. To trigger processing, you have to call :meth:`next_event`.
-
- Args:
- data (:term:`bytes-like object`):
- The new data that was just received.
-
- Special case: If *data* is an empty byte-string like ``b""``,
- then this indicates that the remote side has closed the
- connection (end of file). Normally this is convenient, because
- standard Python APIs like :meth:`file.read` or
- :meth:`socket.recv` use ``b""`` to indicate end-of-file, while
- other failures to read are indicated using other mechanisms
- like raising :exc:`TimeoutError`. When using such an API you
- can just blindly pass through whatever you get from ``read``
- to :meth:`receive_data`, and everything will work.
-
- But, if you have an API where reading an empty string is a
- valid non-EOF condition, then you need to be aware of this and
- make sure to check for such strings and avoid passing them to
- :meth:`receive_data`.
-
- Returns:
- Nothing, but after calling this you should call :meth:`next_event`
- to parse the newly received data.
-
- Raises:
- RuntimeError:
- Raised if you pass an empty *data*, indicating EOF, and then
- pass a non-empty *data*, indicating more data that somehow
- arrived after the EOF.
-
- (Calling ``receive_data(b"")`` multiple times is fine,
- and equivalent to calling it once.)
-
- """
- if data:
- if self._receive_buffer_closed:
- raise RuntimeError("received close, then received more data?")
- self._receive_buffer += data
- else:
- self._receive_buffer_closed = True
-
- def _extract_next_receive_event(self):
- state = self.their_state
- # We don't pause immediately when they enter DONE, because even in
- # DONE state we can still process a ConnectionClosed() event. But
- # if we have data in our buffer, then we definitely aren't getting
- # a ConnectionClosed() immediately and we need to pause.
- if state is DONE and self._receive_buffer:
- return PAUSED
- if state is MIGHT_SWITCH_PROTOCOL or state is SWITCHED_PROTOCOL:
- return PAUSED
- assert self._reader is not None
- event = self._reader(self._receive_buffer)
- if event is None:
- if not self._receive_buffer and self._receive_buffer_closed:
- # In some unusual cases (basically just HTTP/1.0 bodies), EOF
- # triggers an actual protocol event; in that case, we want to
- # return that event, and then the state will change and we'll
- # get called again to generate the actual ConnectionClosed().
- if hasattr(self._reader, "read_eof"):
- event = self._reader.read_eof()
- else:
- event = ConnectionClosed()
- if event is None:
- event = NEED_DATA
- return event
-
- def next_event(self):
- """Parse the next event out of our receive buffer, update our internal
- state, and return it.
-
- This is a mutating operation -- think of it like calling :func:`next`
- on an iterator.
-
- Returns:
- : One of three things:
-
- 1) An event object -- see :ref:`events`.
-
- 2) The special constant :data:`NEED_DATA`, which indicates that
- you need to read more data from your socket and pass it to
- :meth:`receive_data` before this method will be able to return
- any more events.
-
- 3) The special constant :data:`PAUSED`, which indicates that we
- are not in a state where we can process incoming data (usually
- because the peer has finished their part of the current
- request/response cycle, and you have not yet called
- :meth:`start_next_cycle`). See :ref:`flow-control` for details.
-
- Raises:
- RemoteProtocolError:
- The peer has misbehaved. You should close the connection
- (possibly after sending some kind of 4xx response).
-
- Once this method returns :class:`ConnectionClosed` once, then all
- subsequent calls will also return :class:`ConnectionClosed`.
-
- If this method raises any exception besides :exc:`RemoteProtocolError`
- then that's a bug -- if it happens please file a bug report!
-
- If this method raises any exception then it also sets
- :attr:`Connection.their_state` to :data:`ERROR` -- see
- :ref:`error-handling` for discussion.
-
- """
-
- if self.their_state is ERROR:
- raise RemoteProtocolError("Can't receive data when peer state is ERROR")
- try:
- event = self._extract_next_receive_event()
- if event not in [NEED_DATA, PAUSED]:
- self._process_event(self.their_role, event)
- if event is NEED_DATA:
- if len(self._receive_buffer) > self._max_incomplete_event_size:
- # 431 is "Request header fields too large" which is pretty
- # much the only situation where we can get here
- raise RemoteProtocolError(
- "Receive buffer too long", error_status_hint=431
- )
- if self._receive_buffer_closed:
- # We're still trying to complete some event, but that's
- # never going to happen because no more data is coming
- raise RemoteProtocolError("peer unexpectedly closed connection")
- return event
- except BaseException as exc:
- self._process_error(self.their_role)
- if isinstance(exc, LocalProtocolError):
- exc._reraise_as_remote_protocol_error()
- else:
- raise
-
- def send(self, event):
- """Convert a high-level event into bytes that can be sent to the peer,
- while updating our internal state machine.
-
- Args:
- event: The :ref:`event ` to send.
-
- Returns:
- If ``type(event) is ConnectionClosed``, then returns
- ``None``. Otherwise, returns a :term:`bytes-like object`.
-
- Raises:
- LocalProtocolError:
- Sending this event at this time would violate our
- understanding of the HTTP/1.1 protocol.
-
- If this method raises any exception then it also sets
- :attr:`Connection.our_state` to :data:`ERROR` -- see
- :ref:`error-handling` for discussion.
-
- """
- data_list = self.send_with_data_passthrough(event)
- if data_list is None:
- return None
- else:
- return b"".join(data_list)
-
- def send_with_data_passthrough(self, event):
- """Identical to :meth:`send`, except that in situations where
- :meth:`send` returns a single :term:`bytes-like object`, this instead
- returns a list of them -- and when sending a :class:`Data` event, this
- list is guaranteed to contain the exact object you passed in as
- :attr:`Data.data`. See :ref:`sendfile` for discussion.
-
- """
- if self.our_state is ERROR:
- raise LocalProtocolError("Can't send data when our state is ERROR")
- try:
- if type(event) is Response:
- self._clean_up_response_headers_for_sending(event)
- # We want to call _process_event before calling the writer,
- # because if someone tries to do something invalid then this will
- # give a sensible error message, while our writers all just assume
- # they will only receive valid events. But, _process_event might
- # change self._writer. So we have to do a little dance:
- writer = self._writer
- self._process_event(self.our_role, event)
- if type(event) is ConnectionClosed:
- return None
- else:
- # In any situation where writer is None, process_event should
- # have raised ProtocolError
- assert writer is not None
- data_list = []
- writer(event, data_list.append)
- return data_list
- except:
- self._process_error(self.our_role)
- raise
-
- def send_failed(self):
- """Notify the state machine that we failed to send the data it gave
- us.
-
- This causes :attr:`Connection.our_state` to immediately become
- :data:`ERROR` -- see :ref:`error-handling` for discussion.
-
- """
- self._process_error(self.our_role)
-
- # When sending a Response, we take responsibility for a few things:
- #
- # - Sometimes you MUST set Connection: close. We take care of those
- # times. (You can also set it yourself if you want, and if you do then
- # we'll respect that and close the connection at the right time. But you
- # don't have to worry about that unless you want to.)
- #
- # - The user has to set Content-Length if they want it. Otherwise, for
- # responses that have bodies (e.g. not HEAD), then we will automatically
- # select the right mechanism for streaming a body of unknown length,
- # which depends on depending on the peer's HTTP version.
- #
- # This function's *only* responsibility is making sure headers are set up
- # right -- everything downstream just looks at the headers. There are no
- # side channels. It mutates the response event in-place (but not the
- # response.headers list object).
- def _clean_up_response_headers_for_sending(self, response):
- assert type(response) is Response
-
- headers = response.headers
- need_close = False
-
- # HEAD requests need some special handling: they always act like they
- # have Content-Length: 0, and that's how _body_framing treats
- # them. But their headers are supposed to match what we would send if
- # the request was a GET. (Technically there is one deviation allowed:
- # we're allowed to leave out the framing headers -- see
- # https://tools.ietf.org/html/rfc7231#section-4.3.2 . But it's just as
- # easy to get them right.)
- method_for_choosing_headers = self._request_method
- if method_for_choosing_headers == b"HEAD":
- method_for_choosing_headers = b"GET"
- framing_type, _ = _body_framing(method_for_choosing_headers, response)
- if framing_type in ("chunked", "http/1.0"):
- # This response has a body of unknown length.
- # If our peer is HTTP/1.1, we use Transfer-Encoding: chunked
- # If our peer is HTTP/1.0, we use no framing headers, and close the
- # connection afterwards.
- #
- # Make sure to clear Content-Length (in principle user could have
- # set both and then we ignored Content-Length b/c
- # Transfer-Encoding overwrote it -- this would be naughty of them,
- # but the HTTP spec says that if our peer does this then we have
- # to fix it instead of erroring out, so we'll accord the user the
- # same respect).
- headers = set_comma_header(headers, b"content-length", [])
- if self.their_http_version is None or self.their_http_version < b"1.1":
- # Either we never got a valid request and are sending back an
- # error (their_http_version is None), so we assume the worst;
- # or else we did get a valid HTTP/1.0 request, so we know that
- # they don't understand chunked encoding.
- headers = set_comma_header(headers, b"transfer-encoding", [])
- # This is actually redundant ATM, since currently we
- # unconditionally disable keep-alive when talking to HTTP/1.0
- # peers. But let's be defensive just in case we add
- # Connection: keep-alive support later:
- if self._request_method != b"HEAD":
- need_close = True
- else:
- headers = set_comma_header(headers, b"transfer-encoding", ["chunked"])
-
- if not self._cstate.keep_alive or need_close:
- # Make sure Connection: close is set
- connection = set(get_comma_header(headers, b"connection"))
- connection.discard(b"keep-alive")
- connection.add(b"close")
- headers = set_comma_header(headers, b"connection", sorted(connection))
-
- response.headers = headers
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/_events.py b/IKEA_scraper/.venv/Lib/site-packages/h11/_events.py
deleted file mode 100644
index 18279301..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/_events.py
+++ /dev/null
@@ -1,302 +0,0 @@
-# High level events that make up HTTP/1.1 conversations. Loosely inspired by
-# the corresponding events in hyper-h2:
-#
-# http://python-hyper.org/h2/en/stable/api.html#events
-#
-# Don't subclass these. Stuff will break.
-
-import re
-
-from . import _headers
-from ._abnf import request_target
-from ._util import bytesify, LocalProtocolError, validate
-
-# Everything in __all__ gets re-exported as part of the h11 public API.
-__all__ = [
- "Request",
- "InformationalResponse",
- "Response",
- "Data",
- "EndOfMessage",
- "ConnectionClosed",
-]
-
-request_target_re = re.compile(request_target.encode("ascii"))
-
-
-class _EventBundle:
- _fields = []
- _defaults = {}
-
- def __init__(self, **kwargs):
- _parsed = kwargs.pop("_parsed", False)
- allowed = set(self._fields)
- for kwarg in kwargs:
- if kwarg not in allowed:
- raise TypeError(
- "unrecognized kwarg {} for {}".format(
- kwarg, self.__class__.__name__
- )
- )
- required = allowed.difference(self._defaults)
- for field in required:
- if field not in kwargs:
- raise TypeError(
- "missing required kwarg {} for {}".format(
- field, self.__class__.__name__
- )
- )
- self.__dict__.update(self._defaults)
- self.__dict__.update(kwargs)
-
- # Special handling for some fields
-
- if "headers" in self.__dict__:
- self.headers = _headers.normalize_and_validate(
- self.headers, _parsed=_parsed
- )
-
- if not _parsed:
- for field in ["method", "target", "http_version", "reason"]:
- if field in self.__dict__:
- self.__dict__[field] = bytesify(self.__dict__[field])
-
- if "status_code" in self.__dict__:
- if not isinstance(self.status_code, int):
- raise LocalProtocolError("status code must be integer")
- # Because IntEnum objects are instances of int, but aren't
- # duck-compatible (sigh), see gh-72.
- self.status_code = int(self.status_code)
-
- self._validate()
-
- def _validate(self):
- pass
-
- def __repr__(self):
- name = self.__class__.__name__
- kwarg_strs = [
- "{}={}".format(field, self.__dict__[field]) for field in self._fields
- ]
- kwarg_str = ", ".join(kwarg_strs)
- return "{}({})".format(name, kwarg_str)
-
- # Useful for tests
- def __eq__(self, other):
- return self.__class__ == other.__class__ and self.__dict__ == other.__dict__
-
- # This is an unhashable type.
- __hash__ = None
-
-
-class Request(_EventBundle):
- """The beginning of an HTTP request.
-
- Fields:
-
- .. attribute:: method
-
- An HTTP method, e.g. ``b"GET"`` or ``b"POST"``. Always a byte
- string. :term:`Bytes-like objects ` and native
- strings containing only ascii characters will be automatically
- converted to byte strings.
-
- .. attribute:: target
-
- The target of an HTTP request, e.g. ``b"/index.html"``, or one of the
- more exotic formats described in `RFC 7320, section 5.3
- `_. Always a byte
- string. :term:`Bytes-like objects ` and native
- strings containing only ascii characters will be automatically
- converted to byte strings.
-
- .. attribute:: headers
-
- Request headers, represented as a list of (name, value) pairs. See
- :ref:`the header normalization rules ` for details.
-
- .. attribute:: http_version
-
- The HTTP protocol version, represented as a byte string like
- ``b"1.1"``. See :ref:`the HTTP version normalization rules
- ` for details.
-
- """
-
- _fields = ["method", "target", "headers", "http_version"]
- _defaults = {"http_version": b"1.1"}
-
- def _validate(self):
- # "A server MUST respond with a 400 (Bad Request) status code to any
- # HTTP/1.1 request message that lacks a Host header field and to any
- # request message that contains more than one Host header field or a
- # Host header field with an invalid field-value."
- # -- https://tools.ietf.org/html/rfc7230#section-5.4
- host_count = 0
- for name, value in self.headers:
- if name == b"host":
- host_count += 1
- if self.http_version == b"1.1" and host_count == 0:
- raise LocalProtocolError("Missing mandatory Host: header")
- if host_count > 1:
- raise LocalProtocolError("Found multiple Host: headers")
-
- validate(request_target_re, self.target, "Illegal target characters")
-
-
-class _ResponseBase(_EventBundle):
- _fields = ["status_code", "headers", "http_version", "reason"]
- _defaults = {"http_version": b"1.1", "reason": b""}
-
-
-class InformationalResponse(_ResponseBase):
- """An HTTP informational response.
-
- Fields:
-
- .. attribute:: status_code
-
- The status code of this response, as an integer. For an
- :class:`InformationalResponse`, this is always in the range [100,
- 200).
-
- .. attribute:: headers
-
- Request headers, represented as a list of (name, value) pairs. See
- :ref:`the header normalization rules ` for
- details.
-
- .. attribute:: http_version
-
- The HTTP protocol version, represented as a byte string like
- ``b"1.1"``. See :ref:`the HTTP version normalization rules
- ` for details.
-
- .. attribute:: reason
-
- The reason phrase of this response, as a byte string. For example:
- ``b"OK"``, or ``b"Not Found"``.
-
- """
-
- def _validate(self):
- if not (100 <= self.status_code < 200):
- raise LocalProtocolError(
- "InformationalResponse status_code should be in range "
- "[100, 200), not {}".format(self.status_code)
- )
-
-
-class Response(_ResponseBase):
- """The beginning of an HTTP response.
-
- Fields:
-
- .. attribute:: status_code
-
- The status code of this response, as an integer. For an
- :class:`Response`, this is always in the range [200,
- 600).
-
- .. attribute:: headers
-
- Request headers, represented as a list of (name, value) pairs. See
- :ref:`the header normalization rules ` for details.
-
- .. attribute:: http_version
-
- The HTTP protocol version, represented as a byte string like
- ``b"1.1"``. See :ref:`the HTTP version normalization rules
- ` for details.
-
- .. attribute:: reason
-
- The reason phrase of this response, as a byte string. For example:
- ``b"OK"``, or ``b"Not Found"``.
-
- """
-
- def _validate(self):
- if not (200 <= self.status_code < 600):
- raise LocalProtocolError(
- "Response status_code should be in range [200, 600), not {}".format(
- self.status_code
- )
- )
-
-
-class Data(_EventBundle):
- """Part of an HTTP message body.
-
- Fields:
-
- .. attribute:: data
-
- A :term:`bytes-like object` containing part of a message body. Or, if
- using the ``combine=False`` argument to :meth:`Connection.send`, then
- any object that your socket writing code knows what to do with, and for
- which calling :func:`len` returns the number of bytes that will be
- written -- see :ref:`sendfile` for details.
-
- .. attribute:: chunk_start
-
- A marker that indicates whether this data object is from the start of a
- chunked transfer encoding chunk. This field is ignored when when a Data
- event is provided to :meth:`Connection.send`: it is only valid on
- events emitted from :meth:`Connection.next_event`. You probably
- shouldn't use this attribute at all; see
- :ref:`chunk-delimiters-are-bad` for details.
-
- .. attribute:: chunk_end
-
- A marker that indicates whether this data object is the last for a
- given chunked transfer encoding chunk. This field is ignored when when
- a Data event is provided to :meth:`Connection.send`: it is only valid
- on events emitted from :meth:`Connection.next_event`. You probably
- shouldn't use this attribute at all; see
- :ref:`chunk-delimiters-are-bad` for details.
-
- """
-
- _fields = ["data", "chunk_start", "chunk_end"]
- _defaults = {"chunk_start": False, "chunk_end": False}
-
-
-# XX FIXME: "A recipient MUST ignore (or consider as an error) any fields that
-# are forbidden to be sent in a trailer, since processing them as if they were
-# present in the header section might bypass external security filters."
-# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#chunked.trailer.part
-# Unfortunately, the list of forbidden fields is long and vague :-/
-class EndOfMessage(_EventBundle):
- """The end of an HTTP message.
-
- Fields:
-
- .. attribute:: headers
-
- Default value: ``[]``
-
- Any trailing headers attached to this message, represented as a list of
- (name, value) pairs. See :ref:`the header normalization rules
- ` for details.
-
- Must be empty unless ``Transfer-Encoding: chunked`` is in use.
-
- """
-
- _fields = ["headers"]
- _defaults = {"headers": []}
-
-
-class ConnectionClosed(_EventBundle):
- """This event indicates that the sender has closed their outgoing
- connection.
-
- Note that this does not necessarily mean that they can't *receive* further
- data, because TCP connections are composed to two one-way channels which
- can be closed independently. See :ref:`closing` for details.
-
- No fields.
- """
-
- pass
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/_headers.py b/IKEA_scraper/.venv/Lib/site-packages/h11/_headers.py
deleted file mode 100644
index 7ed39bc1..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/_headers.py
+++ /dev/null
@@ -1,242 +0,0 @@
-import re
-
-from ._abnf import field_name, field_value
-from ._util import bytesify, LocalProtocolError, validate
-
-# Facts
-# -----
-#
-# Headers are:
-# keys: case-insensitive ascii
-# values: mixture of ascii and raw bytes
-#
-# "Historically, HTTP has allowed field content with text in the ISO-8859-1
-# charset [ISO-8859-1], supporting other charsets only through use of
-# [RFC2047] encoding. In practice, most HTTP header field values use only a
-# subset of the US-ASCII charset [USASCII]. Newly defined header fields SHOULD
-# limit their field values to US-ASCII octets. A recipient SHOULD treat other
-# octets in field content (obs-text) as opaque data."
-# And it deprecates all non-ascii values
-#
-# Leading/trailing whitespace in header names is forbidden
-#
-# Values get leading/trailing whitespace stripped
-#
-# Content-Disposition actually needs to contain unicode semantically; to
-# accomplish this it has a terrifically weird way of encoding the filename
-# itself as ascii (and even this still has lots of cross-browser
-# incompatibilities)
-#
-# Order is important:
-# "a proxy MUST NOT change the order of these field values when forwarding a
-# message"
-# (and there are several headers where the order indicates a preference)
-#
-# Multiple occurences of the same header:
-# "A sender MUST NOT generate multiple header fields with the same field name
-# in a message unless either the entire field value for that header field is
-# defined as a comma-separated list [or the header is Set-Cookie which gets a
-# special exception]" - RFC 7230. (cookies are in RFC 6265)
-#
-# So every header aside from Set-Cookie can be merged by b", ".join if it
-# occurs repeatedly. But, of course, they can't necessarily be split by
-# .split(b","), because quoting.
-#
-# Given all this mess (case insensitive, duplicates allowed, order is
-# important, ...), there doesn't appear to be any standard way to handle
-# headers in Python -- they're almost like dicts, but... actually just
-# aren't. For now we punt and just use a super simple representation: headers
-# are a list of pairs
-#
-# [(name1, value1), (name2, value2), ...]
-#
-# where all entries are bytestrings, names are lowercase and have no
-# leading/trailing whitespace, and values are bytestrings with no
-# leading/trailing whitespace. Searching and updating are done via naive O(n)
-# methods.
-#
-# Maybe a dict-of-lists would be better?
-
-_content_length_re = re.compile(br"[0-9]+")
-_field_name_re = re.compile(field_name.encode("ascii"))
-_field_value_re = re.compile(field_value.encode("ascii"))
-
-
-class Headers:
- """
- A list-like interface that allows iterating over headers as byte-pairs
- of (lowercased-name, value).
-
- Internally we actually store the representation as three-tuples,
- including both the raw original casing, in order to preserve casing
- over-the-wire, and the lowercased name, for case-insensitive comparisions.
-
- r = Request(
- method="GET",
- target="/",
- headers=[("Host", "example.org"), ("Connection", "keep-alive")],
- http_version="1.1",
- )
- assert r.headers == [
- (b"host", b"example.org"),
- (b"connection", b"keep-alive")
- ]
- assert r.headers.raw_items() == [
- (b"Host", b"example.org"),
- (b"Connection", b"keep-alive")
- ]
- """
-
- __slots__ = "_full_items"
-
- def __init__(self, full_items):
- self._full_items = full_items
-
- def __iter__(self):
- for _, name, value in self._full_items:
- yield name, value
-
- def __bool__(self):
- return bool(self._full_items)
-
- def __eq__(self, other):
- return list(self) == list(other)
-
- def __len__(self):
- return len(self._full_items)
-
- def __repr__(self):
- return "" % repr(list(self))
-
- def __getitem__(self, idx):
- _, name, value = self._full_items[idx]
- return (name, value)
-
- def raw_items(self):
- return [(raw_name, value) for raw_name, _, value in self._full_items]
-
-
-def normalize_and_validate(headers, _parsed=False):
- new_headers = []
- seen_content_length = None
- saw_transfer_encoding = False
- for name, value in headers:
- # For headers coming out of the parser, we can safely skip some steps,
- # because it always returns bytes and has already run these regexes
- # over the data:
- if not _parsed:
- name = bytesify(name)
- value = bytesify(value)
- validate(_field_name_re, name, "Illegal header name {!r}", name)
- validate(_field_value_re, value, "Illegal header value {!r}", value)
- raw_name = name
- name = name.lower()
- if name == b"content-length":
- lengths = {length.strip() for length in value.split(b",")}
- if len(lengths) != 1:
- raise LocalProtocolError("conflicting Content-Length headers")
- value = lengths.pop()
- validate(_content_length_re, value, "bad Content-Length")
- if seen_content_length is None:
- seen_content_length = value
- new_headers.append((raw_name, name, value))
- elif seen_content_length != value:
- raise LocalProtocolError("conflicting Content-Length headers")
- elif name == b"transfer-encoding":
- # "A server that receives a request message with a transfer coding
- # it does not understand SHOULD respond with 501 (Not
- # Implemented)."
- # https://tools.ietf.org/html/rfc7230#section-3.3.1
- if saw_transfer_encoding:
- raise LocalProtocolError(
- "multiple Transfer-Encoding headers", error_status_hint=501
- )
- # "All transfer-coding names are case-insensitive"
- # -- https://tools.ietf.org/html/rfc7230#section-4
- value = value.lower()
- if value != b"chunked":
- raise LocalProtocolError(
- "Only Transfer-Encoding: chunked is supported",
- error_status_hint=501,
- )
- saw_transfer_encoding = True
- new_headers.append((raw_name, name, value))
- else:
- new_headers.append((raw_name, name, value))
- return Headers(new_headers)
-
-
-def get_comma_header(headers, name):
- # Should only be used for headers whose value is a list of
- # comma-separated, case-insensitive values.
- #
- # The header name `name` is expected to be lower-case bytes.
- #
- # Connection: meets these criteria (including cast insensitivity).
- #
- # Content-Length: technically is just a single value (1*DIGIT), but the
- # standard makes reference to implementations that do multiple values, and
- # using this doesn't hurt. Ditto, case insensitivity doesn't things either
- # way.
- #
- # Transfer-Encoding: is more complex (allows for quoted strings), so
- # splitting on , is actually wrong. For example, this is legal:
- #
- # Transfer-Encoding: foo; options="1,2", chunked
- #
- # and should be parsed as
- #
- # foo; options="1,2"
- # chunked
- #
- # but this naive function will parse it as
- #
- # foo; options="1
- # 2"
- # chunked
- #
- # However, this is okay because the only thing we are going to do with
- # any Transfer-Encoding is reject ones that aren't just "chunked", so
- # both of these will be treated the same anyway.
- #
- # Expect: the only legal value is the literal string
- # "100-continue". Splitting on commas is harmless. Case insensitive.
- #
- out = []
- for _, found_name, found_raw_value in headers._full_items:
- if found_name == name:
- found_raw_value = found_raw_value.lower()
- for found_split_value in found_raw_value.split(b","):
- found_split_value = found_split_value.strip()
- if found_split_value:
- out.append(found_split_value)
- return out
-
-
-def set_comma_header(headers, name, new_values):
- # The header name `name` is expected to be lower-case bytes.
- #
- # Note that when we store the header we use title casing for the header
- # names, in order to match the conventional HTTP header style.
- #
- # Simply calling `.title()` is a blunt approach, but it's correct
- # here given the cases where we're using `set_comma_header`...
- #
- # Connection, Content-Length, Transfer-Encoding.
- new_headers = []
- for found_raw_name, found_name, found_raw_value in headers._full_items:
- if found_name != name:
- new_headers.append((found_raw_name, found_raw_value))
- for new_value in new_values:
- new_headers.append((name.title(), new_value))
- return normalize_and_validate(new_headers)
-
-
-def has_expect_100_continue(request):
- # https://tools.ietf.org/html/rfc7231#section-5.1.1
- # "A server that receives a 100-continue expectation in an HTTP/1.0 request
- # MUST ignore that expectation."
- if request.http_version < b"1.1":
- return False
- expect = get_comma_header(request.headers, b"expect")
- return b"100-continue" in expect
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/_readers.py b/IKEA_scraper/.venv/Lib/site-packages/h11/_readers.py
deleted file mode 100644
index 0ead0bec..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/_readers.py
+++ /dev/null
@@ -1,222 +0,0 @@
-# Code to read HTTP data
-#
-# Strategy: each reader is a callable which takes a ReceiveBuffer object, and
-# either:
-# 1) consumes some of it and returns an Event
-# 2) raises a LocalProtocolError (for consistency -- e.g. we call validate()
-# and it might raise a LocalProtocolError, so simpler just to always use
-# this)
-# 3) returns None, meaning "I need more data"
-#
-# If they have a .read_eof attribute, then this will be called if an EOF is
-# received -- but this is optional. Either way, the actual ConnectionClosed
-# event will be generated afterwards.
-#
-# READERS is a dict describing how to pick a reader. It maps states to either:
-# - a reader
-# - or, for body readers, a dict of per-framing reader factories
-
-import re
-
-from ._abnf import chunk_header, header_field, request_line, status_line
-from ._events import *
-from ._state import *
-from ._util import LocalProtocolError, RemoteProtocolError, validate
-
-__all__ = ["READERS"]
-
-header_field_re = re.compile(header_field.encode("ascii"))
-
-# Remember that this has to run in O(n) time -- so e.g. the bytearray cast is
-# critical.
-obs_fold_re = re.compile(br"[ \t]+")
-
-
-def _obsolete_line_fold(lines):
- it = iter(lines)
- last = None
- for line in it:
- match = obs_fold_re.match(line)
- if match:
- if last is None:
- raise LocalProtocolError("continuation line at start of headers")
- if not isinstance(last, bytearray):
- last = bytearray(last)
- last += b" "
- last += line[match.end() :]
- else:
- if last is not None:
- yield last
- last = line
- if last is not None:
- yield last
-
-
-def _decode_header_lines(lines):
- for line in _obsolete_line_fold(lines):
- matches = validate(header_field_re, line, "illegal header line: {!r}", line)
- yield (matches["field_name"], matches["field_value"])
-
-
-request_line_re = re.compile(request_line.encode("ascii"))
-
-
-def maybe_read_from_IDLE_client(buf):
- lines = buf.maybe_extract_lines()
- if lines is None:
- if buf.is_next_line_obviously_invalid_request_line():
- raise LocalProtocolError("illegal request line")
- return None
- if not lines:
- raise LocalProtocolError("no request line received")
- matches = validate(
- request_line_re, lines[0], "illegal request line: {!r}", lines[0]
- )
- return Request(
- headers=list(_decode_header_lines(lines[1:])), _parsed=True, **matches
- )
-
-
-status_line_re = re.compile(status_line.encode("ascii"))
-
-
-def maybe_read_from_SEND_RESPONSE_server(buf):
- lines = buf.maybe_extract_lines()
- if lines is None:
- if buf.is_next_line_obviously_invalid_request_line():
- raise LocalProtocolError("illegal request line")
- return None
- if not lines:
- raise LocalProtocolError("no response line received")
- matches = validate(status_line_re, lines[0], "illegal status line: {!r}", lines[0])
- # Tolerate missing reason phrases
- if matches["reason"] is None:
- matches["reason"] = b""
- status_code = matches["status_code"] = int(matches["status_code"])
- class_ = InformationalResponse if status_code < 200 else Response
- return class_(
- headers=list(_decode_header_lines(lines[1:])), _parsed=True, **matches
- )
-
-
-class ContentLengthReader:
- def __init__(self, length):
- self._length = length
- self._remaining = length
-
- def __call__(self, buf):
- if self._remaining == 0:
- return EndOfMessage()
- data = buf.maybe_extract_at_most(self._remaining)
- if data is None:
- return None
- self._remaining -= len(data)
- return Data(data=data)
-
- def read_eof(self):
- raise RemoteProtocolError(
- "peer closed connection without sending complete message body "
- "(received {} bytes, expected {})".format(
- self._length - self._remaining, self._length
- )
- )
-
-
-chunk_header_re = re.compile(chunk_header.encode("ascii"))
-
-
-class ChunkedReader:
- def __init__(self):
- self._bytes_in_chunk = 0
- # After reading a chunk, we have to throw away the trailing \r\n; if
- # this is >0 then we discard that many bytes before resuming regular
- # de-chunkification.
- self._bytes_to_discard = 0
- self._reading_trailer = False
-
- def __call__(self, buf):
- if self._reading_trailer:
- lines = buf.maybe_extract_lines()
- if lines is None:
- return None
- return EndOfMessage(headers=list(_decode_header_lines(lines)))
- if self._bytes_to_discard > 0:
- data = buf.maybe_extract_at_most(self._bytes_to_discard)
- if data is None:
- return None
- self._bytes_to_discard -= len(data)
- if self._bytes_to_discard > 0:
- return None
- # else, fall through and read some more
- assert self._bytes_to_discard == 0
- if self._bytes_in_chunk == 0:
- # We need to refill our chunk count
- chunk_header = buf.maybe_extract_next_line()
- if chunk_header is None:
- return None
- matches = validate(
- chunk_header_re,
- chunk_header,
- "illegal chunk header: {!r}",
- chunk_header,
- )
- # XX FIXME: we discard chunk extensions. Does anyone care?
- self._bytes_in_chunk = int(matches["chunk_size"], base=16)
- if self._bytes_in_chunk == 0:
- self._reading_trailer = True
- return self(buf)
- chunk_start = True
- else:
- chunk_start = False
- assert self._bytes_in_chunk > 0
- data = buf.maybe_extract_at_most(self._bytes_in_chunk)
- if data is None:
- return None
- self._bytes_in_chunk -= len(data)
- if self._bytes_in_chunk == 0:
- self._bytes_to_discard = 2
- chunk_end = True
- else:
- chunk_end = False
- return Data(data=data, chunk_start=chunk_start, chunk_end=chunk_end)
-
- def read_eof(self):
- raise RemoteProtocolError(
- "peer closed connection without sending complete message body "
- "(incomplete chunked read)"
- )
-
-
-class Http10Reader:
- def __call__(self, buf):
- data = buf.maybe_extract_at_most(999999999)
- if data is None:
- return None
- return Data(data=data)
-
- def read_eof(self):
- return EndOfMessage()
-
-
-def expect_nothing(buf):
- if buf:
- raise LocalProtocolError("Got data when expecting EOF")
- return None
-
-
-READERS = {
- (CLIENT, IDLE): maybe_read_from_IDLE_client,
- (SERVER, IDLE): maybe_read_from_SEND_RESPONSE_server,
- (SERVER, SEND_RESPONSE): maybe_read_from_SEND_RESPONSE_server,
- (CLIENT, DONE): expect_nothing,
- (CLIENT, MUST_CLOSE): expect_nothing,
- (CLIENT, CLOSED): expect_nothing,
- (SERVER, DONE): expect_nothing,
- (SERVER, MUST_CLOSE): expect_nothing,
- (SERVER, CLOSED): expect_nothing,
- SEND_BODY: {
- "chunked": ChunkedReader,
- "content-length": ContentLengthReader,
- "http/1.0": Http10Reader,
- },
-}
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/_receivebuffer.py b/IKEA_scraper/.venv/Lib/site-packages/h11/_receivebuffer.py
deleted file mode 100644
index a3737f35..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/_receivebuffer.py
+++ /dev/null
@@ -1,152 +0,0 @@
-import re
-import sys
-
-__all__ = ["ReceiveBuffer"]
-
-
-# Operations we want to support:
-# - find next \r\n or \r\n\r\n (\n or \n\n are also acceptable),
-# or wait until there is one
-# - read at-most-N bytes
-# Goals:
-# - on average, do this fast
-# - worst case, do this in O(n) where n is the number of bytes processed
-# Plan:
-# - store bytearray, offset, how far we've searched for a separator token
-# - use the how-far-we've-searched data to avoid rescanning
-# - while doing a stream of uninterrupted processing, advance offset instead
-# of constantly copying
-# WARNING:
-# - I haven't benchmarked or profiled any of this yet.
-#
-# Note that starting in Python 3.4, deleting the initial n bytes from a
-# bytearray is amortized O(n), thanks to some excellent work by Antoine
-# Martin:
-#
-# https://bugs.python.org/issue19087
-#
-# This means that if we only supported 3.4+, we could get rid of the code here
-# involving self._start and self.compress, because it's doing exactly the same
-# thing that bytearray now does internally.
-#
-# BUT unfortunately, we still support 2.7, and reading short segments out of a
-# long buffer MUST be O(bytes read) to avoid DoS issues, so we can't actually
-# delete this code. Yet:
-#
-# https://pythonclock.org/
-#
-# (Two things to double-check first though: make sure PyPy also has the
-# optimization, and benchmark to make sure it's a win, since we do have a
-# slightly clever thing where we delay calling compress() until we've
-# processed a whole event, which could in theory be slightly more efficient
-# than the internal bytearray support.)
-blank_line_regex = re.compile(b"\n\r?\n", re.MULTILINE)
-
-
-class ReceiveBuffer:
- def __init__(self):
- self._data = bytearray()
- self._next_line_search = 0
- self._multiple_lines_search = 0
-
- def __iadd__(self, byteslike):
- self._data += byteslike
- return self
-
- def __bool__(self):
- return bool(len(self))
-
- def __len__(self):
- return len(self._data)
-
- # for @property unprocessed_data
- def __bytes__(self):
- return bytes(self._data)
-
- def _extract(self, count):
- # extracting an initial slice of the data buffer and return it
- out = self._data[:count]
- del self._data[:count]
-
- self._next_line_search = 0
- self._multiple_lines_search = 0
-
- return out
-
- def maybe_extract_at_most(self, count):
- """
- Extract a fixed number of bytes from the buffer.
- """
- out = self._data[:count]
- if not out:
- return None
-
- return self._extract(count)
-
- def maybe_extract_next_line(self):
- """
- Extract the first line, if it is completed in the buffer.
- """
- # Only search in buffer space that we've not already looked at.
- search_start_index = max(0, self._next_line_search - 1)
- partial_idx = self._data.find(b"\r\n", search_start_index)
-
- if partial_idx == -1:
- self._next_line_search = len(self._data)
- return None
-
- # + 2 is to compensate len(b"\r\n")
- idx = partial_idx + 2
-
- return self._extract(idx)
-
- def maybe_extract_lines(self):
- """
- Extract everything up to the first blank line, and return a list of lines.
- """
- # Handle the case where we have an immediate empty line.
- if self._data[:1] == b"\n":
- self._extract(1)
- return []
-
- if self._data[:2] == b"\r\n":
- self._extract(2)
- return []
-
- # Only search in buffer space that we've not already looked at.
- match = blank_line_regex.search(self._data, self._multiple_lines_search)
- if match is None:
- self._multiple_lines_search = max(0, len(self._data) - 2)
- return None
-
- # Truncate the buffer and return it.
- idx = match.span(0)[-1]
- out = self._extract(idx)
- lines = out.split(b"\n")
-
- for line in lines:
- if line.endswith(b"\r"):
- del line[-1]
-
- assert lines[-2] == lines[-1] == b""
-
- del lines[-2:]
-
- return lines
-
- # In theory we should wait until `\r\n` before starting to validate
- # incoming data. However it's interesting to detect (very) invalid data
- # early given they might not even contain `\r\n` at all (hence only
- # timeout will get rid of them).
- # This is not a 100% effective detection but more of a cheap sanity check
- # allowing for early abort in some useful cases.
- # This is especially interesting when peer is messing up with HTTPS and
- # sent us a TLS stream where we were expecting plain HTTP given all
- # versions of TLS so far start handshake with a 0x16 message type code.
- def is_next_line_obviously_invalid_request_line(self):
- try:
- # HTTP header line must not contain non-printable characters
- # and should not start with a space
- return self._data[0] < 0x21
- except IndexError:
- return False
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/_state.py b/IKEA_scraper/.venv/Lib/site-packages/h11/_state.py
deleted file mode 100644
index 0f08a090..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/_state.py
+++ /dev/null
@@ -1,307 +0,0 @@
-################################################################
-# The core state machine
-################################################################
-#
-# Rule 1: everything that affects the state machine and state transitions must
-# live here in this file. As much as possible goes into the table-based
-# representation, but for the bits that don't quite fit, the actual code and
-# state must nonetheless live here.
-#
-# Rule 2: this file does not know about what role we're playing; it only knows
-# about HTTP request/response cycles in the abstract. This ensures that we
-# don't cheat and apply different rules to local and remote parties.
-#
-#
-# Theory of operation
-# ===================
-#
-# Possibly the simplest way to think about this is that we actually have 5
-# different state machines here. Yes, 5. These are:
-#
-# 1) The client state, with its complicated automaton (see the docs)
-# 2) The server state, with its complicated automaton (see the docs)
-# 3) The keep-alive state, with possible states {True, False}
-# 4) The SWITCH_CONNECT state, with possible states {False, True}
-# 5) The SWITCH_UPGRADE state, with possible states {False, True}
-#
-# For (3)-(5), the first state listed is the initial state.
-#
-# (1)-(3) are stored explicitly in member variables. The last
-# two are stored implicitly in the pending_switch_proposals set as:
-# (state of 4) == (_SWITCH_CONNECT in pending_switch_proposals)
-# (state of 5) == (_SWITCH_UPGRADE in pending_switch_proposals)
-#
-# And each of these machines has two different kinds of transitions:
-#
-# a) Event-triggered
-# b) State-triggered
-#
-# Event triggered is the obvious thing that you'd think it is: some event
-# happens, and if it's the right event at the right time then a transition
-# happens. But there are somewhat complicated rules for which machines can
-# "see" which events. (As a rule of thumb, if a machine "sees" an event, this
-# means two things: the event can affect the machine, and if the machine is
-# not in a state where it expects that event then it's an error.) These rules
-# are:
-#
-# 1) The client machine sees all h11.events objects emitted by the client.
-#
-# 2) The server machine sees all h11.events objects emitted by the server.
-#
-# It also sees the client's Request event.
-#
-# And sometimes, server events are annotated with a _SWITCH_* event. For
-# example, we can have a (Response, _SWITCH_CONNECT) event, which is
-# different from a regular Response event.
-#
-# 3) The keep-alive machine sees the process_keep_alive_disabled() event
-# (which is derived from Request/Response events), and this event
-# transitions it from True -> False, or from False -> False. There's no way
-# to transition back.
-#
-# 4&5) The _SWITCH_* machines transition from False->True when we get a
-# Request that proposes the relevant type of switch (via
-# process_client_switch_proposals), and they go from True->False when we
-# get a Response that has no _SWITCH_* annotation.
-#
-# So that's event-triggered transitions.
-#
-# State-triggered transitions are less standard. What they do here is couple
-# the machines together. The way this works is, when certain *joint*
-# configurations of states are achieved, then we automatically transition to a
-# new *joint* state. So, for example, if we're ever in a joint state with
-#
-# client: DONE
-# keep-alive: False
-#
-# then the client state immediately transitions to:
-#
-# client: MUST_CLOSE
-#
-# This is fundamentally different from an event-based transition, because it
-# doesn't matter how we arrived at the {client: DONE, keep-alive: False} state
-# -- maybe the client transitioned SEND_BODY -> DONE, or keep-alive
-# transitioned True -> False. Either way, once this precondition is satisfied,
-# this transition is immediately triggered.
-#
-# What if two conflicting state-based transitions get enabled at the same
-# time? In practice there's only one case where this arises (client DONE ->
-# MIGHT_SWITCH_PROTOCOL versus DONE -> MUST_CLOSE), and we resolve it by
-# explicitly prioritizing the DONE -> MIGHT_SWITCH_PROTOCOL transition.
-#
-# Implementation
-# --------------
-#
-# The event-triggered transitions for the server and client machines are all
-# stored explicitly in a table. Ditto for the state-triggered transitions that
-# involve just the server and client state.
-#
-# The transitions for the other machines, and the state-triggered transitions
-# that involve the other machines, are written out as explicit Python code.
-#
-# It'd be nice if there were some cleaner way to do all this. This isn't
-# *too* terrible, but I feel like it could probably be better.
-#
-# WARNING
-# -------
-#
-# The script that generates the state machine diagrams for the docs knows how
-# to read out the EVENT_TRIGGERED_TRANSITIONS and STATE_TRIGGERED_TRANSITIONS
-# tables. But it can't automatically read the transitions that are written
-# directly in Python code. So if you touch those, you need to also update the
-# script to keep it in sync!
-
-from ._events import *
-from ._util import LocalProtocolError, make_sentinel
-
-# Everything in __all__ gets re-exported as part of the h11 public API.
-__all__ = [
- "CLIENT",
- "SERVER",
- "IDLE",
- "SEND_RESPONSE",
- "SEND_BODY",
- "DONE",
- "MUST_CLOSE",
- "CLOSED",
- "MIGHT_SWITCH_PROTOCOL",
- "SWITCHED_PROTOCOL",
- "ERROR",
-]
-
-CLIENT = make_sentinel("CLIENT")
-SERVER = make_sentinel("SERVER")
-
-# States
-IDLE = make_sentinel("IDLE")
-SEND_RESPONSE = make_sentinel("SEND_RESPONSE")
-SEND_BODY = make_sentinel("SEND_BODY")
-DONE = make_sentinel("DONE")
-MUST_CLOSE = make_sentinel("MUST_CLOSE")
-CLOSED = make_sentinel("CLOSED")
-ERROR = make_sentinel("ERROR")
-
-# Switch types
-MIGHT_SWITCH_PROTOCOL = make_sentinel("MIGHT_SWITCH_PROTOCOL")
-SWITCHED_PROTOCOL = make_sentinel("SWITCHED_PROTOCOL")
-
-_SWITCH_UPGRADE = make_sentinel("_SWITCH_UPGRADE")
-_SWITCH_CONNECT = make_sentinel("_SWITCH_CONNECT")
-
-EVENT_TRIGGERED_TRANSITIONS = {
- CLIENT: {
- IDLE: {Request: SEND_BODY, ConnectionClosed: CLOSED},
- SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE},
- DONE: {ConnectionClosed: CLOSED},
- MUST_CLOSE: {ConnectionClosed: CLOSED},
- CLOSED: {ConnectionClosed: CLOSED},
- MIGHT_SWITCH_PROTOCOL: {},
- SWITCHED_PROTOCOL: {},
- ERROR: {},
- },
- SERVER: {
- IDLE: {
- ConnectionClosed: CLOSED,
- Response: SEND_BODY,
- # Special case: server sees client Request events, in this form
- (Request, CLIENT): SEND_RESPONSE,
- },
- SEND_RESPONSE: {
- InformationalResponse: SEND_RESPONSE,
- Response: SEND_BODY,
- (InformationalResponse, _SWITCH_UPGRADE): SWITCHED_PROTOCOL,
- (Response, _SWITCH_CONNECT): SWITCHED_PROTOCOL,
- },
- SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE},
- DONE: {ConnectionClosed: CLOSED},
- MUST_CLOSE: {ConnectionClosed: CLOSED},
- CLOSED: {ConnectionClosed: CLOSED},
- SWITCHED_PROTOCOL: {},
- ERROR: {},
- },
-}
-
-# NB: there are also some special-case state-triggered transitions hard-coded
-# into _fire_state_triggered_transitions below.
-STATE_TRIGGERED_TRANSITIONS = {
- # (Client state, Server state) -> new states
- # Protocol negotiation
- (MIGHT_SWITCH_PROTOCOL, SWITCHED_PROTOCOL): {CLIENT: SWITCHED_PROTOCOL},
- # Socket shutdown
- (CLOSED, DONE): {SERVER: MUST_CLOSE},
- (CLOSED, IDLE): {SERVER: MUST_CLOSE},
- (ERROR, DONE): {SERVER: MUST_CLOSE},
- (DONE, CLOSED): {CLIENT: MUST_CLOSE},
- (IDLE, CLOSED): {CLIENT: MUST_CLOSE},
- (DONE, ERROR): {CLIENT: MUST_CLOSE},
-}
-
-
-class ConnectionState:
- def __init__(self):
- # Extra bits of state that don't quite fit into the state model.
-
- # If this is False then it enables the automatic DONE -> MUST_CLOSE
- # transition. Don't set this directly; call .keep_alive_disabled()
- self.keep_alive = True
-
- # This is a subset of {UPGRADE, CONNECT}, containing the proposals
- # made by the client for switching protocols.
- self.pending_switch_proposals = set()
-
- self.states = {CLIENT: IDLE, SERVER: IDLE}
-
- def process_error(self, role):
- self.states[role] = ERROR
- self._fire_state_triggered_transitions()
-
- def process_keep_alive_disabled(self):
- self.keep_alive = False
- self._fire_state_triggered_transitions()
-
- def process_client_switch_proposal(self, switch_event):
- self.pending_switch_proposals.add(switch_event)
- self._fire_state_triggered_transitions()
-
- def process_event(self, role, event_type, server_switch_event=None):
- if server_switch_event is not None:
- assert role is SERVER
- if server_switch_event not in self.pending_switch_proposals:
- raise LocalProtocolError(
- "Received server {} event without a pending proposal".format(
- server_switch_event
- )
- )
- event_type = (event_type, server_switch_event)
- if server_switch_event is None and event_type is Response:
- self.pending_switch_proposals = set()
- self._fire_event_triggered_transitions(role, event_type)
- # Special case: the server state does get to see Request
- # events.
- if event_type is Request:
- assert role is CLIENT
- self._fire_event_triggered_transitions(SERVER, (Request, CLIENT))
- self._fire_state_triggered_transitions()
-
- def _fire_event_triggered_transitions(self, role, event_type):
- state = self.states[role]
- try:
- new_state = EVENT_TRIGGERED_TRANSITIONS[role][state][event_type]
- except KeyError:
- raise LocalProtocolError(
- "can't handle event type {} when role={} and state={}".format(
- event_type.__name__, role, self.states[role]
- )
- )
- self.states[role] = new_state
-
- def _fire_state_triggered_transitions(self):
- # We apply these rules repeatedly until converging on a fixed point
- while True:
- start_states = dict(self.states)
-
- # It could happen that both these special-case transitions are
- # enabled at the same time:
- #
- # DONE -> MIGHT_SWITCH_PROTOCOL
- # DONE -> MUST_CLOSE
- #
- # For example, this will always be true of a HTTP/1.0 client
- # requesting CONNECT. If this happens, the protocol switch takes
- # priority. From there the client will either go to
- # SWITCHED_PROTOCOL, in which case it's none of our business when
- # they close the connection, or else the server will deny the
- # request, in which case the client will go back to DONE and then
- # from there to MUST_CLOSE.
- if self.pending_switch_proposals:
- if self.states[CLIENT] is DONE:
- self.states[CLIENT] = MIGHT_SWITCH_PROTOCOL
-
- if not self.pending_switch_proposals:
- if self.states[CLIENT] is MIGHT_SWITCH_PROTOCOL:
- self.states[CLIENT] = DONE
-
- if not self.keep_alive:
- for role in (CLIENT, SERVER):
- if self.states[role] is DONE:
- self.states[role] = MUST_CLOSE
-
- # Tabular state-triggered transitions
- joint_state = (self.states[CLIENT], self.states[SERVER])
- changes = STATE_TRIGGERED_TRANSITIONS.get(joint_state, {})
- self.states.update(changes)
-
- if self.states == start_states:
- # Fixed point reached
- return
-
- def start_next_cycle(self):
- if self.states != {CLIENT: DONE, SERVER: DONE}:
- raise LocalProtocolError(
- "not in a reusable state. self.states={}".format(self.states)
- )
- # Can't reach DONE/DONE with any of these active, but still, let's be
- # sure.
- assert self.keep_alive
- assert not self.pending_switch_proposals
- self.states = {CLIENT: IDLE, SERVER: IDLE}
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/_util.py b/IKEA_scraper/.venv/Lib/site-packages/h11/_util.py
deleted file mode 100644
index eb1a5cd9..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/_util.py
+++ /dev/null
@@ -1,122 +0,0 @@
-__all__ = [
- "ProtocolError",
- "LocalProtocolError",
- "RemoteProtocolError",
- "validate",
- "make_sentinel",
- "bytesify",
-]
-
-
-class ProtocolError(Exception):
- """Exception indicating a violation of the HTTP/1.1 protocol.
-
- This as an abstract base class, with two concrete base classes:
- :exc:`LocalProtocolError`, which indicates that you tried to do something
- that HTTP/1.1 says is illegal, and :exc:`RemoteProtocolError`, which
- indicates that the remote peer tried to do something that HTTP/1.1 says is
- illegal. See :ref:`error-handling` for details.
-
- In addition to the normal :exc:`Exception` features, it has one attribute:
-
- .. attribute:: error_status_hint
-
- This gives a suggestion as to what status code a server might use if
- this error occurred as part of a request.
-
- For a :exc:`RemoteProtocolError`, this is useful as a suggestion for
- how you might want to respond to a misbehaving peer, if you're
- implementing a server.
-
- For a :exc:`LocalProtocolError`, this can be taken as a suggestion for
- how your peer might have responded to *you* if h11 had allowed you to
- continue.
-
- The default is 400 Bad Request, a generic catch-all for protocol
- violations.
-
- """
-
- def __init__(self, msg, error_status_hint=400):
- if type(self) is ProtocolError:
- raise TypeError("tried to directly instantiate ProtocolError")
- Exception.__init__(self, msg)
- self.error_status_hint = error_status_hint
-
-
-# Strategy: there are a number of public APIs where a LocalProtocolError can
-# be raised (send(), all the different event constructors, ...), and only one
-# public API where RemoteProtocolError can be raised
-# (receive_data()). Therefore we always raise LocalProtocolError internally,
-# and then receive_data will translate this into a RemoteProtocolError.
-#
-# Internally:
-# LocalProtocolError is the generic "ProtocolError".
-# Externally:
-# LocalProtocolError is for local errors and RemoteProtocolError is for
-# remote errors.
-class LocalProtocolError(ProtocolError):
- def _reraise_as_remote_protocol_error(self):
- # After catching a LocalProtocolError, use this method to re-raise it
- # as a RemoteProtocolError. This method must be called from inside an
- # except: block.
- #
- # An easy way to get an equivalent RemoteProtocolError is just to
- # modify 'self' in place.
- self.__class__ = RemoteProtocolError
- # But the re-raising is somewhat non-trivial -- you might think that
- # now that we've modified the in-flight exception object, that just
- # doing 'raise' to re-raise it would be enough. But it turns out that
- # this doesn't work, because Python tracks the exception type
- # (exc_info[0]) separately from the exception object (exc_info[1]),
- # and we only modified the latter. So we really do need to re-raise
- # the new type explicitly.
- # On py3, the traceback is part of the exception object, so our
- # in-place modification preserved it and we can just re-raise:
- raise self
-
-
-class RemoteProtocolError(ProtocolError):
- pass
-
-
-def validate(regex, data, msg="malformed data", *format_args):
- match = regex.fullmatch(data)
- if not match:
- if format_args:
- msg = msg.format(*format_args)
- raise LocalProtocolError(msg)
- return match.groupdict()
-
-
-# Sentinel values
-#
-# - Inherit identity-based comparison and hashing from object
-# - Have a nice repr
-# - Have a *bonus property*: type(sentinel) is sentinel
-#
-# The bonus property is useful if you want to take the return value from
-# next_event() and do some sort of dispatch based on type(event).
-class _SentinelBase(type):
- def __repr__(self):
- return self.__name__
-
-
-def make_sentinel(name):
- cls = _SentinelBase(name, (_SentinelBase,), {})
- cls.__class__ = cls
- return cls
-
-
-# Used for methods, request targets, HTTP versions, header names, and header
-# values. Accepts ascii-strings, or bytes/bytearray/memoryview/..., and always
-# returns bytes.
-def bytesify(s):
- # Fast-path:
- if type(s) is bytes:
- return s
- if isinstance(s, str):
- s = s.encode("ascii")
- if isinstance(s, int):
- raise TypeError("expected bytes-like object, not int")
- return bytes(s)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/_version.py b/IKEA_scraper/.venv/Lib/site-packages/h11/_version.py
deleted file mode 100644
index cb5c2c32..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/_version.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# This file must be kept very simple, because it is consumed from several
-# places -- it is imported by h11/__init__.py, execfile'd by setup.py, etc.
-
-# We use a simple scheme:
-# 1.0.0 -> 1.0.0+dev -> 1.1.0 -> 1.1.0+dev
-# where the +dev versions are never released into the wild, they're just what
-# we stick into the VCS in between releases.
-#
-# This is compatible with PEP 440:
-# http://legacy.python.org/dev/peps/pep-0440/
-# via the use of the "local suffix" "+dev", which is disallowed on index
-# servers and causes 1.0.0+dev to sort after plain 1.0.0, which is what we
-# want. (Contrast with the special suffix 1.0.0.dev, which sorts *before*
-# 1.0.0.)
-
-__version__ = "0.12.0"
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/_writers.py b/IKEA_scraper/.venv/Lib/site-packages/h11/_writers.py
deleted file mode 100644
index cb5e8a8c..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/_writers.py
+++ /dev/null
@@ -1,123 +0,0 @@
-# Code to read HTTP data
-#
-# Strategy: each writer takes an event + a write-some-bytes function, which is
-# calls.
-#
-# WRITERS is a dict describing how to pick a reader. It maps states to either:
-# - a writer
-# - or, for body writers, a dict of framin-dependent writer factories
-
-from ._events import Data, EndOfMessage
-from ._state import CLIENT, IDLE, SEND_BODY, SEND_RESPONSE, SERVER
-from ._util import LocalProtocolError
-
-__all__ = ["WRITERS"]
-
-
-def write_headers(headers, write):
- # "Since the Host field-value is critical information for handling a
- # request, a user agent SHOULD generate Host as the first header field
- # following the request-line." - RFC 7230
- raw_items = headers._full_items
- for raw_name, name, value in raw_items:
- if name == b"host":
- write(b"%s: %s\r\n" % (raw_name, value))
- for raw_name, name, value in raw_items:
- if name != b"host":
- write(b"%s: %s\r\n" % (raw_name, value))
- write(b"\r\n")
-
-
-def write_request(request, write):
- if request.http_version != b"1.1":
- raise LocalProtocolError("I only send HTTP/1.1")
- write(b"%s %s HTTP/1.1\r\n" % (request.method, request.target))
- write_headers(request.headers, write)
-
-
-# Shared between InformationalResponse and Response
-def write_any_response(response, write):
- if response.http_version != b"1.1":
- raise LocalProtocolError("I only send HTTP/1.1")
- status_bytes = str(response.status_code).encode("ascii")
- # We don't bother sending ascii status messages like "OK"; they're
- # optional and ignored by the protocol. (But the space after the numeric
- # status code is mandatory.)
- #
- # XX FIXME: could at least make an effort to pull out the status message
- # from stdlib's http.HTTPStatus table. Or maybe just steal their enums
- # (either by import or copy/paste). We already accept them as status codes
- # since they're of type IntEnum < int.
- write(b"HTTP/1.1 %s %s\r\n" % (status_bytes, response.reason))
- write_headers(response.headers, write)
-
-
-class BodyWriter:
- def __call__(self, event, write):
- if type(event) is Data:
- self.send_data(event.data, write)
- elif type(event) is EndOfMessage:
- self.send_eom(event.headers, write)
- else: # pragma: no cover
- assert False
-
-
-#
-# These are all careful not to do anything to 'data' except call len(data) and
-# write(data). This allows us to transparently pass-through funny objects,
-# like placeholder objects referring to files on disk that will be sent via
-# sendfile(2).
-#
-class ContentLengthWriter(BodyWriter):
- def __init__(self, length):
- self._length = length
-
- def send_data(self, data, write):
- self._length -= len(data)
- if self._length < 0:
- raise LocalProtocolError("Too much data for declared Content-Length")
- write(data)
-
- def send_eom(self, headers, write):
- if self._length != 0:
- raise LocalProtocolError("Too little data for declared Content-Length")
- if headers:
- raise LocalProtocolError("Content-Length and trailers don't mix")
-
-
-class ChunkedWriter(BodyWriter):
- def send_data(self, data, write):
- # if we encoded 0-length data in the naive way, it would look like an
- # end-of-message.
- if not data:
- return
- write(b"%x\r\n" % len(data))
- write(data)
- write(b"\r\n")
-
- def send_eom(self, headers, write):
- write(b"0\r\n")
- write_headers(headers, write)
-
-
-class Http10Writer(BodyWriter):
- def send_data(self, data, write):
- write(data)
-
- def send_eom(self, headers, write):
- if headers:
- raise LocalProtocolError("can't send trailers to HTTP/1.0 client")
- # no need to close the socket ourselves, that will be taken care of by
- # Connection: close machinery
-
-
-WRITERS = {
- (CLIENT, IDLE): write_request,
- (SERVER, IDLE): write_any_response,
- (SERVER, SEND_RESPONSE): write_any_response,
- SEND_BODY: {
- "chunked": ChunkedWriter,
- "content-length": ContentLengthWriter,
- "http/1.0": Http10Writer,
- },
-}
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__init__.py b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/__init__.cpython-39.pyc
deleted file mode 100644
index 36953e6d..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/__init__.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/helpers.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/helpers.cpython-39.pyc
deleted file mode 100644
index 6f8121a5..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/helpers.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_against_stdlib_http.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_against_stdlib_http.cpython-39.pyc
deleted file mode 100644
index 42d6e863..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_against_stdlib_http.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_connection.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_connection.cpython-39.pyc
deleted file mode 100644
index 64c4e8ed..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_connection.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_events.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_events.cpython-39.pyc
deleted file mode 100644
index 7edcce99..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_events.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_headers.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_headers.cpython-39.pyc
deleted file mode 100644
index 4949f079..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_headers.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_helpers.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_helpers.cpython-39.pyc
deleted file mode 100644
index ac436bc5..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_helpers.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_io.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_io.cpython-39.pyc
deleted file mode 100644
index 1e1e7620..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_io.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_receivebuffer.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_receivebuffer.cpython-39.pyc
deleted file mode 100644
index 2a855b6a..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_receivebuffer.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_state.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_state.cpython-39.pyc
deleted file mode 100644
index f0294ac1..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_state.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_util.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_util.cpython-39.pyc
deleted file mode 100644
index 0c6334af..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/__pycache__/test_util.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/data/test-file b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/data/test-file
deleted file mode 100644
index d0be0a6c..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/data/test-file
+++ /dev/null
@@ -1 +0,0 @@
-92b12bc045050b55b848d37167a1a63947c364579889ce1d39788e45e9fac9e5
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/helpers.py b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/helpers.py
deleted file mode 100644
index 9d2cf380..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/helpers.py
+++ /dev/null
@@ -1,77 +0,0 @@
-from .._connection import *
-from .._events import *
-from .._state import *
-
-
-def get_all_events(conn):
- got_events = []
- while True:
- event = conn.next_event()
- if event in (NEED_DATA, PAUSED):
- break
- got_events.append(event)
- if type(event) is ConnectionClosed:
- break
- return got_events
-
-
-def receive_and_get(conn, data):
- conn.receive_data(data)
- return get_all_events(conn)
-
-
-# Merges adjacent Data events, converts payloads to bytestrings, and removes
-# chunk boundaries.
-def normalize_data_events(in_events):
- out_events = []
- for event in in_events:
- if type(event) is Data:
- event.data = bytes(event.data)
- event.chunk_start = False
- event.chunk_end = False
- if out_events and type(out_events[-1]) is type(event) is Data:
- out_events[-1].data += event.data
- else:
- out_events.append(event)
- return out_events
-
-
-# Given that we want to write tests that push some events through a Connection
-# and check that its state updates appropriately... we might as make a habit
-# of pushing them through two Connections with a fake network link in
-# between.
-class ConnectionPair:
- def __init__(self):
- self.conn = {CLIENT: Connection(CLIENT), SERVER: Connection(SERVER)}
- self.other = {CLIENT: SERVER, SERVER: CLIENT}
-
- @property
- def conns(self):
- return self.conn.values()
-
- # expect="match" if expect=send_events; expect=[...] to say what expected
- def send(self, role, send_events, expect="match"):
- if not isinstance(send_events, list):
- send_events = [send_events]
- data = b""
- closed = False
- for send_event in send_events:
- new_data = self.conn[role].send(send_event)
- if new_data is None:
- closed = True
- else:
- data += new_data
- # send uses b"" to mean b"", and None to mean closed
- # receive uses b"" to mean closed, and None to mean "try again"
- # so we have to translate between the two conventions
- if data:
- self.conn[self.other[role]].receive_data(data)
- if closed:
- self.conn[self.other[role]].receive_data(b"")
- got_events = get_all_events(self.conn[self.other[role]])
- if expect == "match":
- expect = send_events
- if not isinstance(expect, list):
- expect = [expect]
- assert got_events == expect
- return data
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_against_stdlib_http.py b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_against_stdlib_http.py
deleted file mode 100644
index e6c5db44..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_against_stdlib_http.py
+++ /dev/null
@@ -1,111 +0,0 @@
-import json
-import os.path
-import socket
-import socketserver
-import threading
-from contextlib import closing, contextmanager
-from http.server import SimpleHTTPRequestHandler
-from urllib.request import urlopen
-
-import h11
-
-
-@contextmanager
-def socket_server(handler):
- httpd = socketserver.TCPServer(("127.0.0.1", 0), handler)
- thread = threading.Thread(
- target=httpd.serve_forever, kwargs={"poll_interval": 0.01}
- )
- thread.daemon = True
- try:
- thread.start()
- yield httpd
- finally:
- httpd.shutdown()
-
-
-test_file_path = os.path.join(os.path.dirname(__file__), "data/test-file")
-with open(test_file_path, "rb") as f:
- test_file_data = f.read()
-
-
-class SingleMindedRequestHandler(SimpleHTTPRequestHandler):
- def translate_path(self, path):
- return test_file_path
-
-
-def test_h11_as_client():
- with socket_server(SingleMindedRequestHandler) as httpd:
- with closing(socket.create_connection(httpd.server_address)) as s:
- c = h11.Connection(h11.CLIENT)
-
- s.sendall(
- c.send(
- h11.Request(
- method="GET", target="/foo", headers=[("Host", "localhost")]
- )
- )
- )
- s.sendall(c.send(h11.EndOfMessage()))
-
- data = bytearray()
- while True:
- event = c.next_event()
- print(event)
- if event is h11.NEED_DATA:
- # Use a small read buffer to make things more challenging
- # and exercise more paths :-)
- c.receive_data(s.recv(10))
- continue
- if type(event) is h11.Response:
- assert event.status_code == 200
- if type(event) is h11.Data:
- data += event.data
- if type(event) is h11.EndOfMessage:
- break
- assert bytes(data) == test_file_data
-
-
-class H11RequestHandler(socketserver.BaseRequestHandler):
- def handle(self):
- with closing(self.request) as s:
- c = h11.Connection(h11.SERVER)
- request = None
- while True:
- event = c.next_event()
- if event is h11.NEED_DATA:
- # Use a small read buffer to make things more challenging
- # and exercise more paths :-)
- c.receive_data(s.recv(10))
- continue
- if type(event) is h11.Request:
- request = event
- if type(event) is h11.EndOfMessage:
- break
- info = json.dumps(
- {
- "method": request.method.decode("ascii"),
- "target": request.target.decode("ascii"),
- "headers": {
- name.decode("ascii"): value.decode("ascii")
- for (name, value) in request.headers
- },
- }
- )
- s.sendall(c.send(h11.Response(status_code=200, headers=[])))
- s.sendall(c.send(h11.Data(data=info.encode("ascii"))))
- s.sendall(c.send(h11.EndOfMessage()))
-
-
-def test_h11_as_server():
- with socket_server(H11RequestHandler) as httpd:
- host, port = httpd.server_address
- url = "http://{}:{}/some-path".format(host, port)
- with closing(urlopen(url)) as f:
- assert f.getcode() == 200
- data = f.read()
- info = json.loads(data.decode("ascii"))
- print(info)
- assert info["method"] == "GET"
- assert info["target"] == "/some-path"
- assert "urllib" in info["headers"]["user-agent"]
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_connection.py b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_connection.py
deleted file mode 100644
index baadec8d..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_connection.py
+++ /dev/null
@@ -1,1078 +0,0 @@
-import pytest
-
-from .._connection import _body_framing, _keep_alive, Connection, NEED_DATA, PAUSED
-from .._events import *
-from .._state import *
-from .._util import LocalProtocolError, RemoteProtocolError
-from .helpers import ConnectionPair, get_all_events, receive_and_get
-
-
-def test__keep_alive():
- assert _keep_alive(
- Request(method="GET", target="/", headers=[("Host", "Example.com")])
- )
- assert not _keep_alive(
- Request(
- method="GET",
- target="/",
- headers=[("Host", "Example.com"), ("Connection", "close")],
- )
- )
- assert not _keep_alive(
- Request(
- method="GET",
- target="/",
- headers=[("Host", "Example.com"), ("Connection", "a, b, cLOse, foo")],
- )
- )
- assert not _keep_alive(
- Request(method="GET", target="/", headers=[], http_version="1.0")
- )
-
- assert _keep_alive(Response(status_code=200, headers=[]))
- assert not _keep_alive(Response(status_code=200, headers=[("Connection", "close")]))
- assert not _keep_alive(
- Response(status_code=200, headers=[("Connection", "a, b, cLOse, foo")])
- )
- assert not _keep_alive(Response(status_code=200, headers=[], http_version="1.0"))
-
-
-def test__body_framing():
- def headers(cl, te):
- headers = []
- if cl is not None:
- headers.append(("Content-Length", str(cl)))
- if te:
- headers.append(("Transfer-Encoding", "chunked"))
- return headers
-
- def resp(status_code=200, cl=None, te=False):
- return Response(status_code=status_code, headers=headers(cl, te))
-
- def req(cl=None, te=False):
- h = headers(cl, te)
- h += [("Host", "example.com")]
- return Request(method="GET", target="/", headers=h)
-
- # Special cases where the headers are ignored:
- for kwargs in [{}, {"cl": 100}, {"te": True}, {"cl": 100, "te": True}]:
- for meth, r in [
- (b"HEAD", resp(**kwargs)),
- (b"GET", resp(status_code=204, **kwargs)),
- (b"GET", resp(status_code=304, **kwargs)),
- ]:
- assert _body_framing(meth, r) == ("content-length", (0,))
-
- # Transfer-encoding
- for kwargs in [{"te": True}, {"cl": 100, "te": True}]:
- for meth, r in [(None, req(**kwargs)), (b"GET", resp(**kwargs))]:
- assert _body_framing(meth, r) == ("chunked", ())
-
- # Content-Length
- for meth, r in [(None, req(cl=100)), (b"GET", resp(cl=100))]:
- assert _body_framing(meth, r) == ("content-length", (100,))
-
- # No headers
- assert _body_framing(None, req()) == ("content-length", (0,))
- assert _body_framing(b"GET", resp()) == ("http/1.0", ())
-
-
-def test_Connection_basics_and_content_length():
- with pytest.raises(ValueError):
- Connection("CLIENT")
-
- p = ConnectionPair()
- assert p.conn[CLIENT].our_role is CLIENT
- assert p.conn[CLIENT].their_role is SERVER
- assert p.conn[SERVER].our_role is SERVER
- assert p.conn[SERVER].their_role is CLIENT
-
- data = p.send(
- CLIENT,
- Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com"), ("Content-Length", "10")],
- ),
- )
- assert data == (
- b"GET / HTTP/1.1\r\n" b"Host: example.com\r\n" b"Content-Length: 10\r\n\r\n"
- )
-
- for conn in p.conns:
- assert conn.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
- assert p.conn[CLIENT].our_state is SEND_BODY
- assert p.conn[CLIENT].their_state is SEND_RESPONSE
- assert p.conn[SERVER].our_state is SEND_RESPONSE
- assert p.conn[SERVER].their_state is SEND_BODY
-
- assert p.conn[CLIENT].their_http_version is None
- assert p.conn[SERVER].their_http_version == b"1.1"
-
- data = p.send(SERVER, InformationalResponse(status_code=100, headers=[]))
- assert data == b"HTTP/1.1 100 \r\n\r\n"
-
- data = p.send(SERVER, Response(status_code=200, headers=[("Content-Length", "11")]))
- assert data == b"HTTP/1.1 200 \r\nContent-Length: 11\r\n\r\n"
-
- for conn in p.conns:
- assert conn.states == {CLIENT: SEND_BODY, SERVER: SEND_BODY}
-
- assert p.conn[CLIENT].their_http_version == b"1.1"
- assert p.conn[SERVER].their_http_version == b"1.1"
-
- data = p.send(CLIENT, Data(data=b"12345"))
- assert data == b"12345"
- data = p.send(
- CLIENT, Data(data=b"67890"), expect=[Data(data=b"67890"), EndOfMessage()]
- )
- assert data == b"67890"
- data = p.send(CLIENT, EndOfMessage(), expect=[])
- assert data == b""
-
- for conn in p.conns:
- assert conn.states == {CLIENT: DONE, SERVER: SEND_BODY}
-
- data = p.send(SERVER, Data(data=b"1234567890"))
- assert data == b"1234567890"
- data = p.send(SERVER, Data(data=b"1"), expect=[Data(data=b"1"), EndOfMessage()])
- assert data == b"1"
- data = p.send(SERVER, EndOfMessage(), expect=[])
- assert data == b""
-
- for conn in p.conns:
- assert conn.states == {CLIENT: DONE, SERVER: DONE}
-
-
-def test_chunked():
- p = ConnectionPair()
-
- p.send(
- CLIENT,
- Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com"), ("Transfer-Encoding", "chunked")],
- ),
- )
- data = p.send(CLIENT, Data(data=b"1234567890", chunk_start=True, chunk_end=True))
- assert data == b"a\r\n1234567890\r\n"
- data = p.send(CLIENT, Data(data=b"abcde", chunk_start=True, chunk_end=True))
- assert data == b"5\r\nabcde\r\n"
- data = p.send(CLIENT, Data(data=b""), expect=[])
- assert data == b""
- data = p.send(CLIENT, EndOfMessage(headers=[("hello", "there")]))
- assert data == b"0\r\nhello: there\r\n\r\n"
-
- p.send(
- SERVER, Response(status_code=200, headers=[("Transfer-Encoding", "chunked")])
- )
- p.send(SERVER, Data(data=b"54321", chunk_start=True, chunk_end=True))
- p.send(SERVER, Data(data=b"12345", chunk_start=True, chunk_end=True))
- p.send(SERVER, EndOfMessage())
-
- for conn in p.conns:
- assert conn.states == {CLIENT: DONE, SERVER: DONE}
-
-
-def test_chunk_boundaries():
- conn = Connection(our_role=SERVER)
-
- request = (
- b"POST / HTTP/1.1\r\n"
- b"Host: example.com\r\n"
- b"Transfer-Encoding: chunked\r\n"
- b"\r\n"
- )
- conn.receive_data(request)
- assert conn.next_event() == Request(
- method="POST",
- target="/",
- headers=[("Host", "example.com"), ("Transfer-Encoding", "chunked")],
- )
- assert conn.next_event() is NEED_DATA
-
- conn.receive_data(b"5\r\nhello\r\n")
- assert conn.next_event() == Data(data=b"hello", chunk_start=True, chunk_end=True)
-
- conn.receive_data(b"5\r\nhel")
- assert conn.next_event() == Data(data=b"hel", chunk_start=True, chunk_end=False)
-
- conn.receive_data(b"l")
- assert conn.next_event() == Data(data=b"l", chunk_start=False, chunk_end=False)
-
- conn.receive_data(b"o\r\n")
- assert conn.next_event() == Data(data=b"o", chunk_start=False, chunk_end=True)
-
- conn.receive_data(b"5\r\nhello")
- assert conn.next_event() == Data(data=b"hello", chunk_start=True, chunk_end=True)
-
- conn.receive_data(b"\r\n")
- assert conn.next_event() == NEED_DATA
-
- conn.receive_data(b"0\r\n\r\n")
- assert conn.next_event() == EndOfMessage()
-
-
-def test_client_talking_to_http10_server():
- c = Connection(CLIENT)
- c.send(Request(method="GET", target="/", headers=[("Host", "example.com")]))
- c.send(EndOfMessage())
- assert c.our_state is DONE
- # No content-length, so Http10 framing for body
- assert receive_and_get(c, b"HTTP/1.0 200 OK\r\n\r\n") == [
- Response(status_code=200, headers=[], http_version="1.0", reason=b"OK")
- ]
- assert c.our_state is MUST_CLOSE
- assert receive_and_get(c, b"12345") == [Data(data=b"12345")]
- assert receive_and_get(c, b"67890") == [Data(data=b"67890")]
- assert receive_and_get(c, b"") == [EndOfMessage(), ConnectionClosed()]
- assert c.their_state is CLOSED
-
-
-def test_server_talking_to_http10_client():
- c = Connection(SERVER)
- # No content-length, so no body
- # NB: no host header
- assert receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n") == [
- Request(method="GET", target="/", headers=[], http_version="1.0"),
- EndOfMessage(),
- ]
- assert c.their_state is MUST_CLOSE
-
- # We automatically Connection: close back at them
- assert (
- c.send(Response(status_code=200, headers=[]))
- == b"HTTP/1.1 200 \r\nConnection: close\r\n\r\n"
- )
-
- assert c.send(Data(data=b"12345")) == b"12345"
- assert c.send(EndOfMessage()) == b""
- assert c.our_state is MUST_CLOSE
-
- # Check that it works if they do send Content-Length
- c = Connection(SERVER)
- # NB: no host header
- assert receive_and_get(c, b"POST / HTTP/1.0\r\nContent-Length: 10\r\n\r\n1") == [
- Request(
- method="POST",
- target="/",
- headers=[("Content-Length", "10")],
- http_version="1.0",
- ),
- Data(data=b"1"),
- ]
- assert receive_and_get(c, b"234567890") == [Data(data=b"234567890"), EndOfMessage()]
- assert c.their_state is MUST_CLOSE
- assert receive_and_get(c, b"") == [ConnectionClosed()]
-
-
-def test_automatic_transfer_encoding_in_response():
- # Check that in responses, the user can specify either Transfer-Encoding:
- # chunked or no framing at all, and in both cases we automatically select
- # the right option depending on whether the peer speaks HTTP/1.0 or
- # HTTP/1.1
- for user_headers in [
- [("Transfer-Encoding", "chunked")],
- [],
- # In fact, this even works if Content-Length is set,
- # because if both are set then Transfer-Encoding wins
- [("Transfer-Encoding", "chunked"), ("Content-Length", "100")],
- ]:
- p = ConnectionPair()
- p.send(
- CLIENT,
- [
- Request(method="GET", target="/", headers=[("Host", "example.com")]),
- EndOfMessage(),
- ],
- )
- # When speaking to HTTP/1.1 client, all of the above cases get
- # normalized to Transfer-Encoding: chunked
- p.send(
- SERVER,
- Response(status_code=200, headers=user_headers),
- expect=Response(
- status_code=200, headers=[("Transfer-Encoding", "chunked")]
- ),
- )
-
- # When speaking to HTTP/1.0 client, all of the above cases get
- # normalized to no-framing-headers
- c = Connection(SERVER)
- receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n")
- assert (
- c.send(Response(status_code=200, headers=user_headers))
- == b"HTTP/1.1 200 \r\nConnection: close\r\n\r\n"
- )
- assert c.send(Data(data=b"12345")) == b"12345"
-
-
-def test_automagic_connection_close_handling():
- p = ConnectionPair()
- # If the user explicitly sets Connection: close, then we notice and
- # respect it
- p.send(
- CLIENT,
- [
- Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com"), ("Connection", "close")],
- ),
- EndOfMessage(),
- ],
- )
- for conn in p.conns:
- assert conn.states[CLIENT] is MUST_CLOSE
- # And if the client sets it, the server automatically echoes it back
- p.send(
- SERVER,
- # no header here...
- [Response(status_code=204, headers=[]), EndOfMessage()],
- # ...but oh look, it arrived anyway
- expect=[
- Response(status_code=204, headers=[("connection", "close")]),
- EndOfMessage(),
- ],
- )
- for conn in p.conns:
- assert conn.states == {CLIENT: MUST_CLOSE, SERVER: MUST_CLOSE}
-
-
-def test_100_continue():
- def setup():
- p = ConnectionPair()
- p.send(
- CLIENT,
- Request(
- method="GET",
- target="/",
- headers=[
- ("Host", "example.com"),
- ("Content-Length", "100"),
- ("Expect", "100-continue"),
- ],
- ),
- )
- for conn in p.conns:
- assert conn.client_is_waiting_for_100_continue
- assert not p.conn[CLIENT].they_are_waiting_for_100_continue
- assert p.conn[SERVER].they_are_waiting_for_100_continue
- return p
-
- # Disabled by 100 Continue
- p = setup()
- p.send(SERVER, InformationalResponse(status_code=100, headers=[]))
- for conn in p.conns:
- assert not conn.client_is_waiting_for_100_continue
- assert not conn.they_are_waiting_for_100_continue
-
- # Disabled by a real response
- p = setup()
- p.send(
- SERVER, Response(status_code=200, headers=[("Transfer-Encoding", "chunked")])
- )
- for conn in p.conns:
- assert not conn.client_is_waiting_for_100_continue
- assert not conn.they_are_waiting_for_100_continue
-
- # Disabled by the client going ahead and sending stuff anyway
- p = setup()
- p.send(CLIENT, Data(data=b"12345"))
- for conn in p.conns:
- assert not conn.client_is_waiting_for_100_continue
- assert not conn.they_are_waiting_for_100_continue
-
-
-def test_max_incomplete_event_size_countermeasure():
- # Infinitely long headers are definitely not okay
- c = Connection(SERVER)
- c.receive_data(b"GET / HTTP/1.0\r\nEndless: ")
- assert c.next_event() is NEED_DATA
- with pytest.raises(RemoteProtocolError):
- while True:
- c.receive_data(b"a" * 1024)
- c.next_event()
-
- # Checking that the same header is accepted / rejected depending on the
- # max_incomplete_event_size setting:
- c = Connection(SERVER, max_incomplete_event_size=5000)
- c.receive_data(b"GET / HTTP/1.0\r\nBig: ")
- c.receive_data(b"a" * 4000)
- c.receive_data(b"\r\n\r\n")
- assert get_all_events(c) == [
- Request(
- method="GET", target="/", http_version="1.0", headers=[("big", "a" * 4000)]
- ),
- EndOfMessage(),
- ]
-
- c = Connection(SERVER, max_incomplete_event_size=4000)
- c.receive_data(b"GET / HTTP/1.0\r\nBig: ")
- c.receive_data(b"a" * 4000)
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
- # Temporarily exceeding the size limit is fine, as long as its done with
- # complete events:
- c = Connection(SERVER, max_incomplete_event_size=5000)
- c.receive_data(b"GET / HTTP/1.0\r\nContent-Length: 10000")
- c.receive_data(b"\r\n\r\n" + b"a" * 10000)
- assert get_all_events(c) == [
- Request(
- method="GET",
- target="/",
- http_version="1.0",
- headers=[("Content-Length", "10000")],
- ),
- Data(data=b"a" * 10000),
- EndOfMessage(),
- ]
-
- c = Connection(SERVER, max_incomplete_event_size=100)
- # Two pipelined requests to create a way-too-big receive buffer... but
- # it's fine because we're not checking
- c.receive_data(
- b"GET /1 HTTP/1.1\r\nHost: a\r\n\r\n"
- b"GET /2 HTTP/1.1\r\nHost: b\r\n\r\n" + b"X" * 1000
- )
- assert get_all_events(c) == [
- Request(method="GET", target="/1", headers=[("host", "a")]),
- EndOfMessage(),
- ]
- # Even more data comes in, still no problem
- c.receive_data(b"X" * 1000)
- # We can respond and reuse to get the second pipelined request
- c.send(Response(status_code=200, headers=[]))
- c.send(EndOfMessage())
- c.start_next_cycle()
- assert get_all_events(c) == [
- Request(method="GET", target="/2", headers=[("host", "b")]),
- EndOfMessage(),
- ]
- # But once we unpause and try to read the next message, and find that it's
- # incomplete and the buffer is *still* way too large, then *that's* a
- # problem:
- c.send(Response(status_code=200, headers=[]))
- c.send(EndOfMessage())
- c.start_next_cycle()
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
-
-def test_reuse_simple():
- p = ConnectionPair()
- p.send(
- CLIENT,
- [Request(method="GET", target="/", headers=[("Host", "a")]), EndOfMessage()],
- )
- p.send(SERVER, [Response(status_code=200, headers=[]), EndOfMessage()])
- for conn in p.conns:
- assert conn.states == {CLIENT: DONE, SERVER: DONE}
- conn.start_next_cycle()
-
- p.send(
- CLIENT,
- [
- Request(method="DELETE", target="/foo", headers=[("Host", "a")]),
- EndOfMessage(),
- ],
- )
- p.send(SERVER, [Response(status_code=404, headers=[]), EndOfMessage()])
-
-
-def test_pipelining():
- # Client doesn't support pipelining, so we have to do this by hand
- c = Connection(SERVER)
- assert c.next_event() is NEED_DATA
- # 3 requests all bunched up
- c.receive_data(
- b"GET /1 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n"
- b"12345"
- b"GET /2 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n"
- b"67890"
- b"GET /3 HTTP/1.1\r\nHost: a.com\r\n\r\n"
- )
- assert get_all_events(c) == [
- Request(
- method="GET",
- target="/1",
- headers=[("Host", "a.com"), ("Content-Length", "5")],
- ),
- Data(data=b"12345"),
- EndOfMessage(),
- ]
- assert c.their_state is DONE
- assert c.our_state is SEND_RESPONSE
-
- assert c.next_event() is PAUSED
-
- c.send(Response(status_code=200, headers=[]))
- c.send(EndOfMessage())
- assert c.their_state is DONE
- assert c.our_state is DONE
-
- c.start_next_cycle()
-
- assert get_all_events(c) == [
- Request(
- method="GET",
- target="/2",
- headers=[("Host", "a.com"), ("Content-Length", "5")],
- ),
- Data(data=b"67890"),
- EndOfMessage(),
- ]
- assert c.next_event() is PAUSED
- c.send(Response(status_code=200, headers=[]))
- c.send(EndOfMessage())
- c.start_next_cycle()
-
- assert get_all_events(c) == [
- Request(method="GET", target="/3", headers=[("Host", "a.com")]),
- EndOfMessage(),
- ]
- # Doesn't pause this time, no trailing data
- assert c.next_event() is NEED_DATA
- c.send(Response(status_code=200, headers=[]))
- c.send(EndOfMessage())
-
- # Arrival of more data triggers pause
- assert c.next_event() is NEED_DATA
- c.receive_data(b"SADF")
- assert c.next_event() is PAUSED
- assert c.trailing_data == (b"SADF", False)
- # If EOF arrives while paused, we don't see that either:
- c.receive_data(b"")
- assert c.trailing_data == (b"SADF", True)
- assert c.next_event() is PAUSED
- c.receive_data(b"")
- assert c.next_event() is PAUSED
- # Can't call receive_data with non-empty buf after closing it
- with pytest.raises(RuntimeError):
- c.receive_data(b"FDSA")
-
-
-def test_protocol_switch():
- for (req, deny, accept) in [
- (
- Request(
- method="CONNECT",
- target="example.com:443",
- headers=[("Host", "foo"), ("Content-Length", "1")],
- ),
- Response(status_code=404, headers=[]),
- Response(status_code=200, headers=[]),
- ),
- (
- Request(
- method="GET",
- target="/",
- headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")],
- ),
- Response(status_code=200, headers=[]),
- InformationalResponse(status_code=101, headers=[("Upgrade", "a")]),
- ),
- (
- Request(
- method="CONNECT",
- target="example.com:443",
- headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")],
- ),
- Response(status_code=404, headers=[]),
- # Accept CONNECT, not upgrade
- Response(status_code=200, headers=[]),
- ),
- (
- Request(
- method="CONNECT",
- target="example.com:443",
- headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")],
- ),
- Response(status_code=404, headers=[]),
- # Accept Upgrade, not CONNECT
- InformationalResponse(status_code=101, headers=[("Upgrade", "b")]),
- ),
- ]:
-
- def setup():
- p = ConnectionPair()
- p.send(CLIENT, req)
- # No switch-related state change stuff yet; the client has to
- # finish the request before that kicks in
- for conn in p.conns:
- assert conn.states[CLIENT] is SEND_BODY
- p.send(CLIENT, [Data(data=b"1"), EndOfMessage()])
- for conn in p.conns:
- assert conn.states[CLIENT] is MIGHT_SWITCH_PROTOCOL
- assert p.conn[SERVER].next_event() is PAUSED
- return p
-
- # Test deny case
- p = setup()
- p.send(SERVER, deny)
- for conn in p.conns:
- assert conn.states == {CLIENT: DONE, SERVER: SEND_BODY}
- p.send(SERVER, EndOfMessage())
- # Check that re-use is still allowed after a denial
- for conn in p.conns:
- conn.start_next_cycle()
-
- # Test accept case
- p = setup()
- p.send(SERVER, accept)
- for conn in p.conns:
- assert conn.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL}
- conn.receive_data(b"123")
- assert conn.next_event() is PAUSED
- conn.receive_data(b"456")
- assert conn.next_event() is PAUSED
- assert conn.trailing_data == (b"123456", False)
-
- # Pausing in might-switch, then recovery
- # (weird artificial case where the trailing data actually is valid
- # HTTP for some reason, because this makes it easier to test the state
- # logic)
- p = setup()
- sc = p.conn[SERVER]
- sc.receive_data(b"GET / HTTP/1.0\r\n\r\n")
- assert sc.next_event() is PAUSED
- assert sc.trailing_data == (b"GET / HTTP/1.0\r\n\r\n", False)
- sc.send(deny)
- assert sc.next_event() is PAUSED
- sc.send(EndOfMessage())
- sc.start_next_cycle()
- assert get_all_events(sc) == [
- Request(method="GET", target="/", headers=[], http_version="1.0"),
- EndOfMessage(),
- ]
-
- # When we're DONE, have no trailing data, and the connection gets
- # closed, we report ConnectionClosed(). When we're in might-switch or
- # switched, we don't.
- p = setup()
- sc = p.conn[SERVER]
- sc.receive_data(b"")
- assert sc.next_event() is PAUSED
- assert sc.trailing_data == (b"", True)
- p.send(SERVER, accept)
- assert sc.next_event() is PAUSED
-
- p = setup()
- sc = p.conn[SERVER]
- sc.receive_data(b"") == []
- assert sc.next_event() is PAUSED
- sc.send(deny)
- assert sc.next_event() == ConnectionClosed()
-
- # You can't send after switching protocols, or while waiting for a
- # protocol switch
- p = setup()
- with pytest.raises(LocalProtocolError):
- p.conn[CLIENT].send(
- Request(method="GET", target="/", headers=[("Host", "a")])
- )
- p = setup()
- p.send(SERVER, accept)
- with pytest.raises(LocalProtocolError):
- p.conn[SERVER].send(Data(data=b"123"))
-
-
-def test_close_simple():
- # Just immediately closing a new connection without anything having
- # happened yet.
- for (who_shot_first, who_shot_second) in [(CLIENT, SERVER), (SERVER, CLIENT)]:
-
- def setup():
- p = ConnectionPair()
- p.send(who_shot_first, ConnectionClosed())
- for conn in p.conns:
- assert conn.states == {
- who_shot_first: CLOSED,
- who_shot_second: MUST_CLOSE,
- }
- return p
-
- # You can keep putting b"" into a closed connection, and you keep
- # getting ConnectionClosed() out:
- p = setup()
- assert p.conn[who_shot_second].next_event() == ConnectionClosed()
- assert p.conn[who_shot_second].next_event() == ConnectionClosed()
- p.conn[who_shot_second].receive_data(b"")
- assert p.conn[who_shot_second].next_event() == ConnectionClosed()
- # Second party can close...
- p = setup()
- p.send(who_shot_second, ConnectionClosed())
- for conn in p.conns:
- assert conn.our_state is CLOSED
- assert conn.their_state is CLOSED
- # But trying to receive new data on a closed connection is a
- # RuntimeError (not ProtocolError, because the problem here isn't
- # violation of HTTP, it's violation of physics)
- p = setup()
- with pytest.raises(RuntimeError):
- p.conn[who_shot_second].receive_data(b"123")
- # And receiving new data on a MUST_CLOSE connection is a ProtocolError
- p = setup()
- p.conn[who_shot_first].receive_data(b"GET")
- with pytest.raises(RemoteProtocolError):
- p.conn[who_shot_first].next_event()
-
-
-def test_close_different_states():
- req = [
- Request(method="GET", target="/foo", headers=[("Host", "a")]),
- EndOfMessage(),
- ]
- resp = [Response(status_code=200, headers=[]), EndOfMessage()]
-
- # Client before request
- p = ConnectionPair()
- p.send(CLIENT, ConnectionClosed())
- for conn in p.conns:
- assert conn.states == {CLIENT: CLOSED, SERVER: MUST_CLOSE}
-
- # Client after request
- p = ConnectionPair()
- p.send(CLIENT, req)
- p.send(CLIENT, ConnectionClosed())
- for conn in p.conns:
- assert conn.states == {CLIENT: CLOSED, SERVER: SEND_RESPONSE}
-
- # Server after request -> not allowed
- p = ConnectionPair()
- p.send(CLIENT, req)
- with pytest.raises(LocalProtocolError):
- p.conn[SERVER].send(ConnectionClosed())
- p.conn[CLIENT].receive_data(b"")
- with pytest.raises(RemoteProtocolError):
- p.conn[CLIENT].next_event()
-
- # Server after response
- p = ConnectionPair()
- p.send(CLIENT, req)
- p.send(SERVER, resp)
- p.send(SERVER, ConnectionClosed())
- for conn in p.conns:
- assert conn.states == {CLIENT: MUST_CLOSE, SERVER: CLOSED}
-
- # Both after closing (ConnectionClosed() is idempotent)
- p = ConnectionPair()
- p.send(CLIENT, req)
- p.send(SERVER, resp)
- p.send(CLIENT, ConnectionClosed())
- p.send(SERVER, ConnectionClosed())
- p.send(CLIENT, ConnectionClosed())
- p.send(SERVER, ConnectionClosed())
-
- # In the middle of sending -> not allowed
- p = ConnectionPair()
- p.send(
- CLIENT,
- Request(
- method="GET", target="/", headers=[("Host", "a"), ("Content-Length", "10")]
- ),
- )
- with pytest.raises(LocalProtocolError):
- p.conn[CLIENT].send(ConnectionClosed())
- p.conn[SERVER].receive_data(b"")
- with pytest.raises(RemoteProtocolError):
- p.conn[SERVER].next_event()
-
-
-# Receive several requests and then client shuts down their side of the
-# connection; we can respond to each
-def test_pipelined_close():
- c = Connection(SERVER)
- # 2 requests then a close
- c.receive_data(
- b"GET /1 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n"
- b"12345"
- b"GET /2 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n"
- b"67890"
- )
- c.receive_data(b"")
- assert get_all_events(c) == [
- Request(
- method="GET",
- target="/1",
- headers=[("host", "a.com"), ("content-length", "5")],
- ),
- Data(data=b"12345"),
- EndOfMessage(),
- ]
- assert c.states[CLIENT] is DONE
- c.send(Response(status_code=200, headers=[]))
- c.send(EndOfMessage())
- assert c.states[SERVER] is DONE
- c.start_next_cycle()
- assert get_all_events(c) == [
- Request(
- method="GET",
- target="/2",
- headers=[("host", "a.com"), ("content-length", "5")],
- ),
- Data(data=b"67890"),
- EndOfMessage(),
- ConnectionClosed(),
- ]
- assert c.states == {CLIENT: CLOSED, SERVER: SEND_RESPONSE}
- c.send(Response(status_code=200, headers=[]))
- c.send(EndOfMessage())
- assert c.states == {CLIENT: CLOSED, SERVER: MUST_CLOSE}
- c.send(ConnectionClosed())
- assert c.states == {CLIENT: CLOSED, SERVER: CLOSED}
-
-
-def test_sendfile():
- class SendfilePlaceholder:
- def __len__(self):
- return 10
-
- placeholder = SendfilePlaceholder()
-
- def setup(header, http_version):
- c = Connection(SERVER)
- receive_and_get(
- c, "GET / HTTP/{}\r\nHost: a\r\n\r\n".format(http_version).encode("ascii")
- )
- headers = []
- if header:
- headers.append(header)
- c.send(Response(status_code=200, headers=headers))
- return c, c.send_with_data_passthrough(Data(data=placeholder))
-
- c, data = setup(("Content-Length", "10"), "1.1")
- assert data == [placeholder]
- # Raises an error if the connection object doesn't think we've sent
- # exactly 10 bytes
- c.send(EndOfMessage())
-
- _, data = setup(("Transfer-Encoding", "chunked"), "1.1")
- assert placeholder in data
- data[data.index(placeholder)] = b"x" * 10
- assert b"".join(data) == b"a\r\nxxxxxxxxxx\r\n"
-
- c, data = setup(None, "1.0")
- assert data == [placeholder]
- assert c.our_state is SEND_BODY
-
-
-def test_errors():
- # After a receive error, you can't receive
- for role in [CLIENT, SERVER]:
- c = Connection(our_role=role)
- c.receive_data(b"gibberish\r\n\r\n")
- with pytest.raises(RemoteProtocolError):
- c.next_event()
- # Now any attempt to receive continues to raise
- assert c.their_state is ERROR
- assert c.our_state is not ERROR
- print(c._cstate.states)
- with pytest.raises(RemoteProtocolError):
- c.next_event()
- # But we can still yell at the client for sending us gibberish
- if role is SERVER:
- assert (
- c.send(Response(status_code=400, headers=[]))
- == b"HTTP/1.1 400 \r\nConnection: close\r\n\r\n"
- )
-
- # After an error sending, you can no longer send
- # (This is especially important for things like content-length errors,
- # where there's complex internal state being modified)
- def conn(role):
- c = Connection(our_role=role)
- if role is SERVER:
- # Put it into the state where it *could* send a response...
- receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n")
- assert c.our_state is SEND_RESPONSE
- return c
-
- for role in [CLIENT, SERVER]:
- if role is CLIENT:
- # This HTTP/1.0 request won't be detected as bad until after we go
- # through the state machine and hit the writing code
- good = Request(method="GET", target="/", headers=[("Host", "example.com")])
- bad = Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com")],
- http_version="1.0",
- )
- elif role is SERVER:
- good = Response(status_code=200, headers=[])
- bad = Response(status_code=200, headers=[], http_version="1.0")
- # Make sure 'good' actually is good
- c = conn(role)
- c.send(good)
- assert c.our_state is not ERROR
- # Do that again, but this time sending 'bad' first
- c = conn(role)
- with pytest.raises(LocalProtocolError):
- c.send(bad)
- assert c.our_state is ERROR
- assert c.their_state is not ERROR
- # Now 'good' is not so good
- with pytest.raises(LocalProtocolError):
- c.send(good)
-
- # And check send_failed() too
- c = conn(role)
- c.send_failed()
- assert c.our_state is ERROR
- assert c.their_state is not ERROR
- # This is idempotent
- c.send_failed()
- assert c.our_state is ERROR
- assert c.their_state is not ERROR
-
-
-def test_idle_receive_nothing():
- # At one point this incorrectly raised an error
- for role in [CLIENT, SERVER]:
- c = Connection(role)
- assert c.next_event() is NEED_DATA
-
-
-def test_connection_drop():
- c = Connection(SERVER)
- c.receive_data(b"GET /")
- assert c.next_event() is NEED_DATA
- c.receive_data(b"")
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
-
-def test_408_request_timeout():
- # Should be able to send this spontaneously as a server without seeing
- # anything from client
- p = ConnectionPair()
- p.send(SERVER, Response(status_code=408, headers=[]))
-
-
-# This used to raise IndexError
-def test_empty_request():
- c = Connection(SERVER)
- c.receive_data(b"\r\n")
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
-
-# This used to raise IndexError
-def test_empty_response():
- c = Connection(CLIENT)
- c.send(Request(method="GET", target="/", headers=[("Host", "a")]))
- c.receive_data(b"\r\n")
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
-
-@pytest.mark.parametrize(
- "data",
- [
- b"\x00",
- b"\x20",
- b"\x16\x03\x01\x00\xa5", # Typical start of a TLS Client Hello
- ],
-)
-def test_early_detection_of_invalid_request(data):
- c = Connection(SERVER)
- # Early detection should occur before even receiving a `\r\n`
- c.receive_data(data)
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
-
-@pytest.mark.parametrize(
- "data",
- [
- b"\x00",
- b"\x20",
- b"\x16\x03\x03\x00\x31", # Typical start of a TLS Server Hello
- ],
-)
-def test_early_detection_of_invalid_response(data):
- c = Connection(CLIENT)
- # Early detection should occur before even receiving a `\r\n`
- c.receive_data(data)
- with pytest.raises(RemoteProtocolError):
- c.next_event()
-
-
-# This used to give different headers for HEAD and GET.
-# The correct way to handle HEAD is to put whatever headers we *would* have
-# put if it were a GET -- even though we know that for HEAD, those headers
-# will be ignored.
-def test_HEAD_framing_headers():
- def setup(method, http_version):
- c = Connection(SERVER)
- c.receive_data(
- method + b" / HTTP/" + http_version + b"\r\n" + b"Host: example.com\r\n\r\n"
- )
- assert type(c.next_event()) is Request
- assert type(c.next_event()) is EndOfMessage
- return c
-
- for method in [b"GET", b"HEAD"]:
- # No Content-Length, HTTP/1.1 peer, should use chunked
- c = setup(method, b"1.1")
- assert (
- c.send(Response(status_code=200, headers=[])) == b"HTTP/1.1 200 \r\n"
- b"Transfer-Encoding: chunked\r\n\r\n"
- )
-
- # No Content-Length, HTTP/1.0 peer, frame with connection: close
- c = setup(method, b"1.0")
- assert (
- c.send(Response(status_code=200, headers=[])) == b"HTTP/1.1 200 \r\n"
- b"Connection: close\r\n\r\n"
- )
-
- # Content-Length + Transfer-Encoding, TE wins
- c = setup(method, b"1.1")
- assert (
- c.send(
- Response(
- status_code=200,
- headers=[
- ("Content-Length", "100"),
- ("Transfer-Encoding", "chunked"),
- ],
- )
- )
- == b"HTTP/1.1 200 \r\n"
- b"Transfer-Encoding: chunked\r\n\r\n"
- )
-
-
-def test_special_exceptions_for_lost_connection_in_message_body():
- c = Connection(SERVER)
- c.receive_data(
- b"POST / HTTP/1.1\r\n" b"Host: example.com\r\n" b"Content-Length: 100\r\n\r\n"
- )
- assert type(c.next_event()) is Request
- assert c.next_event() is NEED_DATA
- c.receive_data(b"12345")
- assert c.next_event() == Data(data=b"12345")
- c.receive_data(b"")
- with pytest.raises(RemoteProtocolError) as excinfo:
- c.next_event()
- assert "received 5 bytes" in str(excinfo.value)
- assert "expected 100" in str(excinfo.value)
-
- c = Connection(SERVER)
- c.receive_data(
- b"POST / HTTP/1.1\r\n"
- b"Host: example.com\r\n"
- b"Transfer-Encoding: chunked\r\n\r\n"
- )
- assert type(c.next_event()) is Request
- assert c.next_event() is NEED_DATA
- c.receive_data(b"8\r\n012345")
- assert c.next_event().data == b"012345"
- c.receive_data(b"")
- with pytest.raises(RemoteProtocolError) as excinfo:
- c.next_event()
- assert "incomplete chunked read" in str(excinfo.value)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_events.py b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_events.py
deleted file mode 100644
index e20f741c..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_events.py
+++ /dev/null
@@ -1,179 +0,0 @@
-from http import HTTPStatus
-
-import pytest
-
-from .. import _events
-from .._events import *
-from .._util import LocalProtocolError
-
-
-def test_event_bundle():
- class T(_events._EventBundle):
- _fields = ["a", "b"]
- _defaults = {"b": 1}
-
- def _validate(self):
- if self.a == 0:
- raise ValueError
-
- # basic construction and methods
- t = T(a=1, b=0)
- assert repr(t) == "T(a=1, b=0)"
- assert t == T(a=1, b=0)
- assert not (t == T(a=2, b=0))
- assert not (t != T(a=1, b=0))
- assert t != T(a=2, b=0)
- with pytest.raises(TypeError):
- hash(t)
-
- # check defaults
- t = T(a=10)
- assert t.a == 10
- assert t.b == 1
-
- # no positional args
- with pytest.raises(TypeError):
- T(1)
-
- with pytest.raises(TypeError):
- T(1, a=1, b=0)
-
- # unknown field
- with pytest.raises(TypeError):
- T(a=1, b=0, c=10)
-
- # missing required field
- with pytest.raises(TypeError) as exc:
- T(b=0)
- # make sure we error on the right missing kwarg
- assert "kwarg a" in str(exc.value)
-
- # _validate is called
- with pytest.raises(ValueError):
- T(a=0, b=0)
-
-
-def test_events():
- with pytest.raises(LocalProtocolError):
- # Missing Host:
- req = Request(
- method="GET", target="/", headers=[("a", "b")], http_version="1.1"
- )
- # But this is okay (HTTP/1.0)
- req = Request(method="GET", target="/", headers=[("a", "b")], http_version="1.0")
- # fields are normalized
- assert req.method == b"GET"
- assert req.target == b"/"
- assert req.headers == [(b"a", b"b")]
- assert req.http_version == b"1.0"
-
- # This is also okay -- has a Host (with weird capitalization, which is ok)
- req = Request(
- method="GET",
- target="/",
- headers=[("a", "b"), ("hOSt", "example.com")],
- http_version="1.1",
- )
- # we normalize header capitalization
- assert req.headers == [(b"a", b"b"), (b"host", b"example.com")]
-
- # Multiple host is bad too
- with pytest.raises(LocalProtocolError):
- req = Request(
- method="GET",
- target="/",
- headers=[("Host", "a"), ("Host", "a")],
- http_version="1.1",
- )
- # Even for HTTP/1.0
- with pytest.raises(LocalProtocolError):
- req = Request(
- method="GET",
- target="/",
- headers=[("Host", "a"), ("Host", "a")],
- http_version="1.0",
- )
-
- # Header values are validated
- for bad_char in "\x00\r\n\f\v":
- with pytest.raises(LocalProtocolError):
- req = Request(
- method="GET",
- target="/",
- headers=[("Host", "a"), ("Foo", "asd" + bad_char)],
- http_version="1.0",
- )
-
- # But for compatibility we allow non-whitespace control characters, even
- # though they're forbidden by the spec.
- Request(
- method="GET",
- target="/",
- headers=[("Host", "a"), ("Foo", "asd\x01\x02\x7f")],
- http_version="1.0",
- )
-
- # Request target is validated
- for bad_char in b"\x00\x20\x7f\xee":
- target = bytearray(b"/")
- target.append(bad_char)
- with pytest.raises(LocalProtocolError):
- Request(
- method="GET", target=target, headers=[("Host", "a")], http_version="1.1"
- )
-
- ir = InformationalResponse(status_code=100, headers=[("Host", "a")])
- assert ir.status_code == 100
- assert ir.headers == [(b"host", b"a")]
- assert ir.http_version == b"1.1"
-
- with pytest.raises(LocalProtocolError):
- InformationalResponse(status_code=200, headers=[("Host", "a")])
-
- resp = Response(status_code=204, headers=[], http_version="1.0")
- assert resp.status_code == 204
- assert resp.headers == []
- assert resp.http_version == b"1.0"
-
- with pytest.raises(LocalProtocolError):
- resp = Response(status_code=100, headers=[], http_version="1.0")
-
- with pytest.raises(LocalProtocolError):
- Response(status_code="100", headers=[], http_version="1.0")
-
- with pytest.raises(LocalProtocolError):
- InformationalResponse(status_code=b"100", headers=[], http_version="1.0")
-
- d = Data(data=b"asdf")
- assert d.data == b"asdf"
-
- eom = EndOfMessage()
- assert eom.headers == []
-
- cc = ConnectionClosed()
- assert repr(cc) == "ConnectionClosed()"
-
-
-def test_intenum_status_code():
- # https://github.com/python-hyper/h11/issues/72
-
- r = Response(status_code=HTTPStatus.OK, headers=[], http_version="1.0")
- assert r.status_code == HTTPStatus.OK
- assert type(r.status_code) is not type(HTTPStatus.OK)
- assert type(r.status_code) is int
-
-
-def test_header_casing():
- r = Request(
- method="GET",
- target="/",
- headers=[("Host", "example.org"), ("Connection", "keep-alive")],
- http_version="1.1",
- )
- assert len(r.headers) == 2
- assert r.headers[0] == (b"host", b"example.org")
- assert r.headers == [(b"host", b"example.org"), (b"connection", b"keep-alive")]
- assert r.headers.raw_items() == [
- (b"Host", b"example.org"),
- (b"Connection", b"keep-alive"),
- ]
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_headers.py b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_headers.py
deleted file mode 100644
index ff3dc8d7..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_headers.py
+++ /dev/null
@@ -1,151 +0,0 @@
-import pytest
-
-from .._headers import *
-
-
-def test_normalize_and_validate():
- assert normalize_and_validate([("foo", "bar")]) == [(b"foo", b"bar")]
- assert normalize_and_validate([(b"foo", b"bar")]) == [(b"foo", b"bar")]
-
- # no leading/trailing whitespace in names
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([(b"foo ", "bar")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([(b" foo", "bar")])
-
- # no weird characters in names
- with pytest.raises(LocalProtocolError) as excinfo:
- normalize_and_validate([(b"foo bar", b"baz")])
- assert "foo bar" in str(excinfo.value)
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([(b"foo\x00bar", b"baz")])
- # Not even 8-bit characters:
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([(b"foo\xffbar", b"baz")])
- # And not even the control characters we allow in values:
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([(b"foo\x01bar", b"baz")])
-
- # no return or NUL characters in values
- with pytest.raises(LocalProtocolError) as excinfo:
- normalize_and_validate([("foo", "bar\rbaz")])
- assert "bar\\rbaz" in str(excinfo.value)
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("foo", "bar\nbaz")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("foo", "bar\x00baz")])
- # no leading/trailing whitespace
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("foo", "barbaz ")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("foo", " barbaz")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("foo", "barbaz\t")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("foo", "\tbarbaz")])
-
- # content-length
- assert normalize_and_validate([("Content-Length", "1")]) == [
- (b"content-length", b"1")
- ]
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("Content-Length", "asdf")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("Content-Length", "1x")])
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("Content-Length", "1"), ("Content-Length", "2")])
- assert normalize_and_validate(
- [("Content-Length", "0"), ("Content-Length", "0")]
- ) == [(b"content-length", b"0")]
- assert normalize_and_validate([("Content-Length", "0 , 0")]) == [
- (b"content-length", b"0")
- ]
- with pytest.raises(LocalProtocolError):
- normalize_and_validate(
- [("Content-Length", "1"), ("Content-Length", "1"), ("Content-Length", "2")]
- )
- with pytest.raises(LocalProtocolError):
- normalize_and_validate([("Content-Length", "1 , 1,2")])
-
- # transfer-encoding
- assert normalize_and_validate([("Transfer-Encoding", "chunked")]) == [
- (b"transfer-encoding", b"chunked")
- ]
- assert normalize_and_validate([("Transfer-Encoding", "cHuNkEd")]) == [
- (b"transfer-encoding", b"chunked")
- ]
- with pytest.raises(LocalProtocolError) as excinfo:
- normalize_and_validate([("Transfer-Encoding", "gzip")])
- assert excinfo.value.error_status_hint == 501 # Not Implemented
- with pytest.raises(LocalProtocolError) as excinfo:
- normalize_and_validate(
- [("Transfer-Encoding", "chunked"), ("Transfer-Encoding", "gzip")]
- )
- assert excinfo.value.error_status_hint == 501 # Not Implemented
-
-
-def test_get_set_comma_header():
- headers = normalize_and_validate(
- [
- ("Connection", "close"),
- ("whatever", "something"),
- ("connectiON", "fOo,, , BAR"),
- ]
- )
-
- assert get_comma_header(headers, b"connection") == [b"close", b"foo", b"bar"]
-
- headers = set_comma_header(headers, b"newthing", ["a", "b"])
-
- with pytest.raises(LocalProtocolError):
- set_comma_header(headers, b"newthing", [" a", "b"])
-
- assert headers == [
- (b"connection", b"close"),
- (b"whatever", b"something"),
- (b"connection", b"fOo,, , BAR"),
- (b"newthing", b"a"),
- (b"newthing", b"b"),
- ]
-
- headers = set_comma_header(headers, b"whatever", ["different thing"])
-
- assert headers == [
- (b"connection", b"close"),
- (b"connection", b"fOo,, , BAR"),
- (b"newthing", b"a"),
- (b"newthing", b"b"),
- (b"whatever", b"different thing"),
- ]
-
-
-def test_has_100_continue():
- from .._events import Request
-
- assert has_expect_100_continue(
- Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com"), ("Expect", "100-continue")],
- )
- )
- assert not has_expect_100_continue(
- Request(method="GET", target="/", headers=[("Host", "example.com")])
- )
- # Case insensitive
- assert has_expect_100_continue(
- Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com"), ("Expect", "100-Continue")],
- )
- )
- # Doesn't work in HTTP/1.0
- assert not has_expect_100_continue(
- Request(
- method="GET",
- target="/",
- headers=[("Host", "example.com"), ("Expect", "100-continue")],
- http_version="1.0",
- )
- )
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_helpers.py b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_helpers.py
deleted file mode 100644
index 1477947a..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_helpers.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from .helpers import *
-
-
-def test_normalize_data_events():
- assert normalize_data_events(
- [
- Data(data=bytearray(b"1")),
- Data(data=b"2"),
- Response(status_code=200, headers=[]),
- Data(data=b"3"),
- Data(data=b"4"),
- EndOfMessage(),
- Data(data=b"5"),
- Data(data=b"6"),
- Data(data=b"7"),
- ]
- ) == [
- Data(data=b"12"),
- Response(status_code=200, headers=[]),
- Data(data=b"34"),
- EndOfMessage(),
- Data(data=b"567"),
- ]
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_io.py b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_io.py
deleted file mode 100644
index 459a627d..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_io.py
+++ /dev/null
@@ -1,544 +0,0 @@
-import pytest
-
-from .._events import *
-from .._headers import Headers, normalize_and_validate
-from .._readers import (
- _obsolete_line_fold,
- ChunkedReader,
- ContentLengthReader,
- Http10Reader,
- READERS,
-)
-from .._receivebuffer import ReceiveBuffer
-from .._state import *
-from .._util import LocalProtocolError
-from .._writers import (
- ChunkedWriter,
- ContentLengthWriter,
- Http10Writer,
- write_any_response,
- write_headers,
- write_request,
- WRITERS,
-)
-from .helpers import normalize_data_events
-
-SIMPLE_CASES = [
- (
- (CLIENT, IDLE),
- Request(
- method="GET",
- target="/a",
- headers=[("Host", "foo"), ("Connection", "close")],
- ),
- b"GET /a HTTP/1.1\r\nHost: foo\r\nConnection: close\r\n\r\n",
- ),
- (
- (SERVER, SEND_RESPONSE),
- Response(status_code=200, headers=[("Connection", "close")], reason=b"OK"),
- b"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n",
- ),
- (
- (SERVER, SEND_RESPONSE),
- Response(status_code=200, headers=[], reason=b"OK"),
- b"HTTP/1.1 200 OK\r\n\r\n",
- ),
- (
- (SERVER, SEND_RESPONSE),
- InformationalResponse(
- status_code=101, headers=[("Upgrade", "websocket")], reason=b"Upgrade"
- ),
- b"HTTP/1.1 101 Upgrade\r\nUpgrade: websocket\r\n\r\n",
- ),
- (
- (SERVER, SEND_RESPONSE),
- InformationalResponse(status_code=101, headers=[], reason=b"Upgrade"),
- b"HTTP/1.1 101 Upgrade\r\n\r\n",
- ),
-]
-
-
-def dowrite(writer, obj):
- got_list = []
- writer(obj, got_list.append)
- return b"".join(got_list)
-
-
-def tw(writer, obj, expected):
- got = dowrite(writer, obj)
- assert got == expected
-
-
-def makebuf(data):
- buf = ReceiveBuffer()
- buf += data
- return buf
-
-
-def tr(reader, data, expected):
- def check(got):
- assert got == expected
- # Headers should always be returned as bytes, not e.g. bytearray
- # https://github.com/python-hyper/wsproto/pull/54#issuecomment-377709478
- for name, value in getattr(got, "headers", []):
- print(name, value)
- assert type(name) is bytes
- assert type(value) is bytes
-
- # Simple: consume whole thing
- buf = makebuf(data)
- check(reader(buf))
- assert not buf
-
- # Incrementally growing buffer
- buf = ReceiveBuffer()
- for i in range(len(data)):
- assert reader(buf) is None
- buf += data[i : i + 1]
- check(reader(buf))
-
- # Trailing data
- buf = makebuf(data)
- buf += b"trailing"
- check(reader(buf))
- assert bytes(buf) == b"trailing"
-
-
-def test_writers_simple():
- for ((role, state), event, binary) in SIMPLE_CASES:
- tw(WRITERS[role, state], event, binary)
-
-
-def test_readers_simple():
- for ((role, state), event, binary) in SIMPLE_CASES:
- tr(READERS[role, state], binary, event)
-
-
-def test_writers_unusual():
- # Simple test of the write_headers utility routine
- tw(
- write_headers,
- normalize_and_validate([("foo", "bar"), ("baz", "quux")]),
- b"foo: bar\r\nbaz: quux\r\n\r\n",
- )
- tw(write_headers, Headers([]), b"\r\n")
-
- # We understand HTTP/1.0, but we don't speak it
- with pytest.raises(LocalProtocolError):
- tw(
- write_request,
- Request(
- method="GET",
- target="/",
- headers=[("Host", "foo"), ("Connection", "close")],
- http_version="1.0",
- ),
- None,
- )
- with pytest.raises(LocalProtocolError):
- tw(
- write_any_response,
- Response(
- status_code=200, headers=[("Connection", "close")], http_version="1.0"
- ),
- None,
- )
-
-
-def test_readers_unusual():
- # Reading HTTP/1.0
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.0\r\nSome: header\r\n\r\n",
- Request(
- method="HEAD",
- target="/foo",
- headers=[("Some", "header")],
- http_version="1.0",
- ),
- )
-
- # check no-headers, since it's only legal with HTTP/1.0
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.0\r\n\r\n",
- Request(method="HEAD", target="/foo", headers=[], http_version="1.0"),
- )
-
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.0 200 OK\r\nSome: header\r\n\r\n",
- Response(
- status_code=200,
- headers=[("Some", "header")],
- http_version="1.0",
- reason=b"OK",
- ),
- )
-
- # single-character header values (actually disallowed by the ABNF in RFC
- # 7230 -- this is a bug in the standard that we originally copied...)
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.0 200 OK\r\n" b"Foo: a a a a a \r\n\r\n",
- Response(
- status_code=200,
- headers=[("Foo", "a a a a a")],
- http_version="1.0",
- reason=b"OK",
- ),
- )
-
- # Empty headers -- also legal
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.0 200 OK\r\n" b"Foo:\r\n\r\n",
- Response(
- status_code=200, headers=[("Foo", "")], http_version="1.0", reason=b"OK"
- ),
- )
-
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.0 200 OK\r\n" b"Foo: \t \t \r\n\r\n",
- Response(
- status_code=200, headers=[("Foo", "")], http_version="1.0", reason=b"OK"
- ),
- )
-
- # Tolerate broken servers that leave off the response code
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.0 200\r\n" b"Foo: bar\r\n\r\n",
- Response(
- status_code=200, headers=[("Foo", "bar")], http_version="1.0", reason=b""
- ),
- )
-
- # Tolerate headers line endings (\r\n and \n)
- # \n\r\b between headers and body
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.1 200 OK\r\nSomeHeader: val\n\r\n",
- Response(
- status_code=200,
- headers=[("SomeHeader", "val")],
- http_version="1.1",
- reason="OK",
- ),
- )
-
- # delimited only with \n
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.1 200 OK\nSomeHeader1: val1\nSomeHeader2: val2\n\n",
- Response(
- status_code=200,
- headers=[("SomeHeader1", "val1"), ("SomeHeader2", "val2")],
- http_version="1.1",
- reason="OK",
- ),
- )
-
- # mixed \r\n and \n
- tr(
- READERS[SERVER, SEND_RESPONSE],
- b"HTTP/1.1 200 OK\r\nSomeHeader1: val1\nSomeHeader2: val2\n\r\n",
- Response(
- status_code=200,
- headers=[("SomeHeader1", "val1"), ("SomeHeader2", "val2")],
- http_version="1.1",
- reason="OK",
- ),
- )
-
- # obsolete line folding
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n"
- b"Host: example.com\r\n"
- b"Some: multi-line\r\n"
- b" header\r\n"
- b"\tnonsense\r\n"
- b" \t \t\tI guess\r\n"
- b"Connection: close\r\n"
- b"More-nonsense: in the\r\n"
- b" last header \r\n\r\n",
- Request(
- method="HEAD",
- target="/foo",
- headers=[
- ("Host", "example.com"),
- ("Some", "multi-line header nonsense I guess"),
- ("Connection", "close"),
- ("More-nonsense", "in the last header"),
- ],
- ),
- )
-
- with pytest.raises(LocalProtocolError):
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n" b" folded: line\r\n\r\n",
- None,
- )
-
- with pytest.raises(LocalProtocolError):
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n" b"foo : line\r\n\r\n",
- None,
- )
- with pytest.raises(LocalProtocolError):
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n" b"foo\t: line\r\n\r\n",
- None,
- )
- with pytest.raises(LocalProtocolError):
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n" b"foo\t: line\r\n\r\n",
- None,
- )
- with pytest.raises(LocalProtocolError):
- tr(READERS[CLIENT, IDLE], b"HEAD /foo HTTP/1.1\r\n" b": line\r\n\r\n", None)
-
-
-def test__obsolete_line_fold_bytes():
- # _obsolete_line_fold has a defensive cast to bytearray, which is
- # necessary to protect against O(n^2) behavior in case anyone ever passes
- # in regular bytestrings... but right now we never pass in regular
- # bytestrings. so this test just exists to get some coverage on that
- # defensive cast.
- assert list(_obsolete_line_fold([b"aaa", b"bbb", b" ccc", b"ddd"])) == [
- b"aaa",
- bytearray(b"bbb ccc"),
- b"ddd",
- ]
-
-
-def _run_reader_iter(reader, buf, do_eof):
- while True:
- event = reader(buf)
- if event is None:
- break
- yield event
- # body readers have undefined behavior after returning EndOfMessage,
- # because this changes the state so they don't get called again
- if type(event) is EndOfMessage:
- break
- if do_eof:
- assert not buf
- yield reader.read_eof()
-
-
-def _run_reader(*args):
- events = list(_run_reader_iter(*args))
- return normalize_data_events(events)
-
-
-def t_body_reader(thunk, data, expected, do_eof=False):
- # Simple: consume whole thing
- print("Test 1")
- buf = makebuf(data)
- assert _run_reader(thunk(), buf, do_eof) == expected
-
- # Incrementally growing buffer
- print("Test 2")
- reader = thunk()
- buf = ReceiveBuffer()
- events = []
- for i in range(len(data)):
- events += _run_reader(reader, buf, False)
- buf += data[i : i + 1]
- events += _run_reader(reader, buf, do_eof)
- assert normalize_data_events(events) == expected
-
- is_complete = any(type(event) is EndOfMessage for event in expected)
- if is_complete and not do_eof:
- buf = makebuf(data + b"trailing")
- assert _run_reader(thunk(), buf, False) == expected
-
-
-def test_ContentLengthReader():
- t_body_reader(lambda: ContentLengthReader(0), b"", [EndOfMessage()])
-
- t_body_reader(
- lambda: ContentLengthReader(10),
- b"0123456789",
- [Data(data=b"0123456789"), EndOfMessage()],
- )
-
-
-def test_Http10Reader():
- t_body_reader(Http10Reader, b"", [EndOfMessage()], do_eof=True)
- t_body_reader(Http10Reader, b"asdf", [Data(data=b"asdf")], do_eof=False)
- t_body_reader(
- Http10Reader, b"asdf", [Data(data=b"asdf"), EndOfMessage()], do_eof=True
- )
-
-
-def test_ChunkedReader():
- t_body_reader(ChunkedReader, b"0\r\n\r\n", [EndOfMessage()])
-
- t_body_reader(
- ChunkedReader,
- b"0\r\nSome: header\r\n\r\n",
- [EndOfMessage(headers=[("Some", "header")])],
- )
-
- t_body_reader(
- ChunkedReader,
- b"5\r\n01234\r\n"
- + b"10\r\n0123456789abcdef\r\n"
- + b"0\r\n"
- + b"Some: header\r\n\r\n",
- [
- Data(data=b"012340123456789abcdef"),
- EndOfMessage(headers=[("Some", "header")]),
- ],
- )
-
- t_body_reader(
- ChunkedReader,
- b"5\r\n01234\r\n" + b"10\r\n0123456789abcdef\r\n" + b"0\r\n\r\n",
- [Data(data=b"012340123456789abcdef"), EndOfMessage()],
- )
-
- # handles upper and lowercase hex
- t_body_reader(
- ChunkedReader,
- b"aA\r\n" + b"x" * 0xAA + b"\r\n" + b"0\r\n\r\n",
- [Data(data=b"x" * 0xAA), EndOfMessage()],
- )
-
- # refuses arbitrarily long chunk integers
- with pytest.raises(LocalProtocolError):
- # Technically this is legal HTTP/1.1, but we refuse to process chunk
- # sizes that don't fit into 20 characters of hex
- t_body_reader(ChunkedReader, b"9" * 100 + b"\r\nxxx", [Data(data=b"xxx")])
-
- # refuses garbage in the chunk count
- with pytest.raises(LocalProtocolError):
- t_body_reader(ChunkedReader, b"10\x00\r\nxxx", None)
-
- # handles (and discards) "chunk extensions" omg wtf
- t_body_reader(
- ChunkedReader,
- b"5; hello=there\r\n"
- + b"xxxxx"
- + b"\r\n"
- + b'0; random="junk"; some=more; canbe=lonnnnngg\r\n\r\n',
- [Data(data=b"xxxxx"), EndOfMessage()],
- )
-
-
-def test_ContentLengthWriter():
- w = ContentLengthWriter(5)
- assert dowrite(w, Data(data=b"123")) == b"123"
- assert dowrite(w, Data(data=b"45")) == b"45"
- assert dowrite(w, EndOfMessage()) == b""
-
- w = ContentLengthWriter(5)
- with pytest.raises(LocalProtocolError):
- dowrite(w, Data(data=b"123456"))
-
- w = ContentLengthWriter(5)
- dowrite(w, Data(data=b"123"))
- with pytest.raises(LocalProtocolError):
- dowrite(w, Data(data=b"456"))
-
- w = ContentLengthWriter(5)
- dowrite(w, Data(data=b"123"))
- with pytest.raises(LocalProtocolError):
- dowrite(w, EndOfMessage())
-
- w = ContentLengthWriter(5)
- dowrite(w, Data(data=b"123")) == b"123"
- dowrite(w, Data(data=b"45")) == b"45"
- with pytest.raises(LocalProtocolError):
- dowrite(w, EndOfMessage(headers=[("Etag", "asdf")]))
-
-
-def test_ChunkedWriter():
- w = ChunkedWriter()
- assert dowrite(w, Data(data=b"aaa")) == b"3\r\naaa\r\n"
- assert dowrite(w, Data(data=b"a" * 20)) == b"14\r\n" + b"a" * 20 + b"\r\n"
-
- assert dowrite(w, Data(data=b"")) == b""
-
- assert dowrite(w, EndOfMessage()) == b"0\r\n\r\n"
-
- assert (
- dowrite(w, EndOfMessage(headers=[("Etag", "asdf"), ("a", "b")]))
- == b"0\r\nEtag: asdf\r\na: b\r\n\r\n"
- )
-
-
-def test_Http10Writer():
- w = Http10Writer()
- assert dowrite(w, Data(data=b"1234")) == b"1234"
- assert dowrite(w, EndOfMessage()) == b""
-
- with pytest.raises(LocalProtocolError):
- dowrite(w, EndOfMessage(headers=[("Etag", "asdf")]))
-
-
-def test_reject_garbage_after_request_line():
- with pytest.raises(LocalProtocolError):
- tr(READERS[SERVER, SEND_RESPONSE], b"HTTP/1.0 200 OK\x00xxxx\r\n\r\n", None)
-
-
-def test_reject_garbage_after_response_line():
- with pytest.raises(LocalProtocolError):
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1 xxxxxx\r\n" b"Host: a\r\n\r\n",
- None,
- )
-
-
-def test_reject_garbage_in_header_line():
- with pytest.raises(LocalProtocolError):
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n" b"Host: foo\x00bar\r\n\r\n",
- None,
- )
-
-
-def test_reject_non_vchar_in_path():
- for bad_char in b"\x00\x20\x7f\xee":
- message = bytearray(b"HEAD /")
- message.append(bad_char)
- message.extend(b" HTTP/1.1\r\nHost: foobar\r\n\r\n")
- with pytest.raises(LocalProtocolError):
- tr(READERS[CLIENT, IDLE], message, None)
-
-
-# https://github.com/python-hyper/h11/issues/57
-def test_allow_some_garbage_in_cookies():
- tr(
- READERS[CLIENT, IDLE],
- b"HEAD /foo HTTP/1.1\r\n"
- b"Host: foo\r\n"
- b"Set-Cookie: ___utmvafIumyLc=kUd\x01UpAt; path=/; Max-Age=900\r\n"
- b"\r\n",
- Request(
- method="HEAD",
- target="/foo",
- headers=[
- ("Host", "foo"),
- ("Set-Cookie", "___utmvafIumyLc=kUd\x01UpAt; path=/; Max-Age=900"),
- ],
- ),
- )
-
-
-def test_host_comes_first():
- tw(
- write_headers,
- normalize_and_validate([("foo", "bar"), ("Host", "example.com")]),
- b"Host: example.com\r\nfoo: bar\r\n\r\n",
- )
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_receivebuffer.py b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_receivebuffer.py
deleted file mode 100644
index 3a61f9dc..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_receivebuffer.py
+++ /dev/null
@@ -1,134 +0,0 @@
-import re
-
-import pytest
-
-from .._receivebuffer import ReceiveBuffer
-
-
-def test_receivebuffer():
- b = ReceiveBuffer()
- assert not b
- assert len(b) == 0
- assert bytes(b) == b""
-
- b += b"123"
- assert b
- assert len(b) == 3
- assert bytes(b) == b"123"
-
- assert bytes(b) == b"123"
-
- assert b.maybe_extract_at_most(2) == b"12"
- assert b
- assert len(b) == 1
- assert bytes(b) == b"3"
-
- assert bytes(b) == b"3"
-
- assert b.maybe_extract_at_most(10) == b"3"
- assert bytes(b) == b""
-
- assert b.maybe_extract_at_most(10) is None
- assert not b
-
- ################################################################
- # maybe_extract_until_next
- ################################################################
-
- b += b"123\n456\r\n789\r\n"
-
- assert b.maybe_extract_next_line() == b"123\n456\r\n"
- assert bytes(b) == b"789\r\n"
-
- assert b.maybe_extract_next_line() == b"789\r\n"
- assert bytes(b) == b""
-
- b += b"12\r"
- assert b.maybe_extract_next_line() is None
- assert bytes(b) == b"12\r"
-
- b += b"345\n\r"
- assert b.maybe_extract_next_line() is None
- assert bytes(b) == b"12\r345\n\r"
-
- # here we stopped at the middle of b"\r\n" delimiter
-
- b += b"\n6789aaa123\r\n"
- assert b.maybe_extract_next_line() == b"12\r345\n\r\n"
- assert b.maybe_extract_next_line() == b"6789aaa123\r\n"
- assert b.maybe_extract_next_line() is None
- assert bytes(b) == b""
-
- ################################################################
- # maybe_extract_lines
- ################################################################
-
- b += b"123\r\na: b\r\nfoo:bar\r\n\r\ntrailing"
- lines = b.maybe_extract_lines()
- assert lines == [b"123", b"a: b", b"foo:bar"]
- assert bytes(b) == b"trailing"
-
- assert b.maybe_extract_lines() is None
-
- b += b"\r\n\r"
- assert b.maybe_extract_lines() is None
-
- assert b.maybe_extract_at_most(100) == b"trailing\r\n\r"
- assert not b
-
- # Empty body case (as happens at the end of chunked encoding if there are
- # no trailing headers, e.g.)
- b += b"\r\ntrailing"
- assert b.maybe_extract_lines() == []
- assert bytes(b) == b"trailing"
-
-
-@pytest.mark.parametrize(
- "data",
- [
- pytest.param(
- (
- b"HTTP/1.1 200 OK\r\n",
- b"Content-type: text/plain\r\n",
- b"Connection: close\r\n",
- b"\r\n",
- b"Some body",
- ),
- id="with_crlf_delimiter",
- ),
- pytest.param(
- (
- b"HTTP/1.1 200 OK\n",
- b"Content-type: text/plain\n",
- b"Connection: close\n",
- b"\n",
- b"Some body",
- ),
- id="with_lf_only_delimiter",
- ),
- pytest.param(
- (
- b"HTTP/1.1 200 OK\n",
- b"Content-type: text/plain\r\n",
- b"Connection: close\n",
- b"\n",
- b"Some body",
- ),
- id="with_mixed_crlf_and_lf",
- ),
- ],
-)
-def test_receivebuffer_for_invalid_delimiter(data):
- b = ReceiveBuffer()
-
- for line in data:
- b += line
-
- lines = b.maybe_extract_lines()
-
- assert lines == [
- b"HTTP/1.1 200 OK",
- b"Content-type: text/plain",
- b"Connection: close",
- ]
- assert bytes(b) == b"Some body"
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_state.py b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_state.py
deleted file mode 100644
index efe83f0a..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_state.py
+++ /dev/null
@@ -1,250 +0,0 @@
-import pytest
-
-from .._events import *
-from .._state import *
-from .._state import _SWITCH_CONNECT, _SWITCH_UPGRADE, ConnectionState
-from .._util import LocalProtocolError
-
-
-def test_ConnectionState():
- cs = ConnectionState()
-
- # Basic event-triggered transitions
-
- assert cs.states == {CLIENT: IDLE, SERVER: IDLE}
-
- cs.process_event(CLIENT, Request)
- # The SERVER-Request special case:
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
-
- # Illegal transitions raise an error and nothing happens
- with pytest.raises(LocalProtocolError):
- cs.process_event(CLIENT, Request)
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
-
- cs.process_event(SERVER, InformationalResponse)
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
-
- cs.process_event(SERVER, Response)
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_BODY}
-
- cs.process_event(CLIENT, EndOfMessage)
- cs.process_event(SERVER, EndOfMessage)
- assert cs.states == {CLIENT: DONE, SERVER: DONE}
-
- # State-triggered transition
-
- cs.process_event(SERVER, ConnectionClosed)
- assert cs.states == {CLIENT: MUST_CLOSE, SERVER: CLOSED}
-
-
-def test_ConnectionState_keep_alive():
- # keep_alive = False
- cs = ConnectionState()
- cs.process_event(CLIENT, Request)
- cs.process_keep_alive_disabled()
- cs.process_event(CLIENT, EndOfMessage)
- assert cs.states == {CLIENT: MUST_CLOSE, SERVER: SEND_RESPONSE}
-
- cs.process_event(SERVER, Response)
- cs.process_event(SERVER, EndOfMessage)
- assert cs.states == {CLIENT: MUST_CLOSE, SERVER: MUST_CLOSE}
-
-
-def test_ConnectionState_keep_alive_in_DONE():
- # Check that if keep_alive is disabled when the CLIENT is already in DONE,
- # then this is sufficient to immediately trigger the DONE -> MUST_CLOSE
- # transition
- cs = ConnectionState()
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, EndOfMessage)
- assert cs.states[CLIENT] is DONE
- cs.process_keep_alive_disabled()
- assert cs.states[CLIENT] is MUST_CLOSE
-
-
-def test_ConnectionState_switch_denied():
- for switch_type in (_SWITCH_CONNECT, _SWITCH_UPGRADE):
- for deny_early in (True, False):
- cs = ConnectionState()
- cs.process_client_switch_proposal(switch_type)
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, Data)
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
-
- assert switch_type in cs.pending_switch_proposals
-
- if deny_early:
- # before client reaches DONE
- cs.process_event(SERVER, Response)
- assert not cs.pending_switch_proposals
-
- cs.process_event(CLIENT, EndOfMessage)
-
- if deny_early:
- assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY}
- else:
- assert cs.states == {
- CLIENT: MIGHT_SWITCH_PROTOCOL,
- SERVER: SEND_RESPONSE,
- }
-
- cs.process_event(SERVER, InformationalResponse)
- assert cs.states == {
- CLIENT: MIGHT_SWITCH_PROTOCOL,
- SERVER: SEND_RESPONSE,
- }
-
- cs.process_event(SERVER, Response)
- assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY}
- assert not cs.pending_switch_proposals
-
-
-_response_type_for_switch = {
- _SWITCH_UPGRADE: InformationalResponse,
- _SWITCH_CONNECT: Response,
- None: Response,
-}
-
-
-def test_ConnectionState_protocol_switch_accepted():
- for switch_event in [_SWITCH_UPGRADE, _SWITCH_CONNECT]:
- cs = ConnectionState()
- cs.process_client_switch_proposal(switch_event)
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, Data)
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
-
- cs.process_event(CLIENT, EndOfMessage)
- assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE}
-
- cs.process_event(SERVER, InformationalResponse)
- assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE}
-
- cs.process_event(SERVER, _response_type_for_switch[switch_event], switch_event)
- assert cs.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL}
-
-
-def test_ConnectionState_double_protocol_switch():
- # CONNECT + Upgrade is legal! Very silly, but legal. So we support
- # it. Because sometimes doing the silly thing is easier than not.
- for server_switch in [None, _SWITCH_UPGRADE, _SWITCH_CONNECT]:
- cs = ConnectionState()
- cs.process_client_switch_proposal(_SWITCH_UPGRADE)
- cs.process_client_switch_proposal(_SWITCH_CONNECT)
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, EndOfMessage)
- assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE}
- cs.process_event(
- SERVER, _response_type_for_switch[server_switch], server_switch
- )
- if server_switch is None:
- assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY}
- else:
- assert cs.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL}
-
-
-def test_ConnectionState_inconsistent_protocol_switch():
- for client_switches, server_switch in [
- ([], _SWITCH_CONNECT),
- ([], _SWITCH_UPGRADE),
- ([_SWITCH_UPGRADE], _SWITCH_CONNECT),
- ([_SWITCH_CONNECT], _SWITCH_UPGRADE),
- ]:
- cs = ConnectionState()
- for client_switch in client_switches:
- cs.process_client_switch_proposal(client_switch)
- cs.process_event(CLIENT, Request)
- with pytest.raises(LocalProtocolError):
- cs.process_event(SERVER, Response, server_switch)
-
-
-def test_ConnectionState_keepalive_protocol_switch_interaction():
- # keep_alive=False + pending_switch_proposals
- cs = ConnectionState()
- cs.process_client_switch_proposal(_SWITCH_UPGRADE)
- cs.process_event(CLIENT, Request)
- cs.process_keep_alive_disabled()
- cs.process_event(CLIENT, Data)
- assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE}
-
- # the protocol switch "wins"
- cs.process_event(CLIENT, EndOfMessage)
- assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE}
-
- # but when the server denies the request, keep_alive comes back into play
- cs.process_event(SERVER, Response)
- assert cs.states == {CLIENT: MUST_CLOSE, SERVER: SEND_BODY}
-
-
-def test_ConnectionState_reuse():
- cs = ConnectionState()
-
- with pytest.raises(LocalProtocolError):
- cs.start_next_cycle()
-
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, EndOfMessage)
-
- with pytest.raises(LocalProtocolError):
- cs.start_next_cycle()
-
- cs.process_event(SERVER, Response)
- cs.process_event(SERVER, EndOfMessage)
-
- cs.start_next_cycle()
- assert cs.states == {CLIENT: IDLE, SERVER: IDLE}
-
- # No keepalive
-
- cs.process_event(CLIENT, Request)
- cs.process_keep_alive_disabled()
- cs.process_event(CLIENT, EndOfMessage)
- cs.process_event(SERVER, Response)
- cs.process_event(SERVER, EndOfMessage)
-
- with pytest.raises(LocalProtocolError):
- cs.start_next_cycle()
-
- # One side closed
-
- cs = ConnectionState()
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, EndOfMessage)
- cs.process_event(CLIENT, ConnectionClosed)
- cs.process_event(SERVER, Response)
- cs.process_event(SERVER, EndOfMessage)
-
- with pytest.raises(LocalProtocolError):
- cs.start_next_cycle()
-
- # Succesful protocol switch
-
- cs = ConnectionState()
- cs.process_client_switch_proposal(_SWITCH_UPGRADE)
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, EndOfMessage)
- cs.process_event(SERVER, InformationalResponse, _SWITCH_UPGRADE)
-
- with pytest.raises(LocalProtocolError):
- cs.start_next_cycle()
-
- # Failed protocol switch
-
- cs = ConnectionState()
- cs.process_client_switch_proposal(_SWITCH_UPGRADE)
- cs.process_event(CLIENT, Request)
- cs.process_event(CLIENT, EndOfMessage)
- cs.process_event(SERVER, Response)
- cs.process_event(SERVER, EndOfMessage)
-
- cs.start_next_cycle()
- assert cs.states == {CLIENT: IDLE, SERVER: IDLE}
-
-
-def test_server_request_is_illegal():
- # There used to be a bug in how we handled the Request special case that
- # made this allowed...
- cs = ConnectionState()
- with pytest.raises(LocalProtocolError):
- cs.process_event(SERVER, Request)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_util.py b/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_util.py
deleted file mode 100644
index d851bdcb..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/h11/tests/test_util.py
+++ /dev/null
@@ -1,99 +0,0 @@
-import re
-import sys
-import traceback
-
-import pytest
-
-from .._util import *
-
-
-def test_ProtocolError():
- with pytest.raises(TypeError):
- ProtocolError("abstract base class")
-
-
-def test_LocalProtocolError():
- try:
- raise LocalProtocolError("foo")
- except LocalProtocolError as e:
- assert str(e) == "foo"
- assert e.error_status_hint == 400
-
- try:
- raise LocalProtocolError("foo", error_status_hint=418)
- except LocalProtocolError as e:
- assert str(e) == "foo"
- assert e.error_status_hint == 418
-
- def thunk():
- raise LocalProtocolError("a", error_status_hint=420)
-
- try:
- try:
- thunk()
- except LocalProtocolError as exc1:
- orig_traceback = "".join(traceback.format_tb(sys.exc_info()[2]))
- exc1._reraise_as_remote_protocol_error()
- except RemoteProtocolError as exc2:
- assert type(exc2) is RemoteProtocolError
- assert exc2.args == ("a",)
- assert exc2.error_status_hint == 420
- new_traceback = "".join(traceback.format_tb(sys.exc_info()[2]))
- assert new_traceback.endswith(orig_traceback)
-
-
-def test_validate():
- my_re = re.compile(br"(?P[0-9]+)\.(?P[0-9]+)")
- with pytest.raises(LocalProtocolError):
- validate(my_re, b"0.")
-
- groups = validate(my_re, b"0.1")
- assert groups == {"group1": b"0", "group2": b"1"}
-
- # successful partial matches are an error - must match whole string
- with pytest.raises(LocalProtocolError):
- validate(my_re, b"0.1xx")
- with pytest.raises(LocalProtocolError):
- validate(my_re, b"0.1\n")
-
-
-def test_validate_formatting():
- my_re = re.compile(br"foo")
-
- with pytest.raises(LocalProtocolError) as excinfo:
- validate(my_re, b"", "oops")
- assert "oops" in str(excinfo.value)
-
- with pytest.raises(LocalProtocolError) as excinfo:
- validate(my_re, b"", "oops {}")
- assert "oops {}" in str(excinfo.value)
-
- with pytest.raises(LocalProtocolError) as excinfo:
- validate(my_re, b"", "oops {} xx", 10)
- assert "oops 10 xx" in str(excinfo.value)
-
-
-def test_make_sentinel():
- S = make_sentinel("S")
- assert repr(S) == "S"
- assert S == S
- assert type(S).__name__ == "S"
- assert S in {S}
- assert type(S) is S
- S2 = make_sentinel("S2")
- assert repr(S2) == "S2"
- assert S != S2
- assert S not in {S2}
- assert type(S) is not type(S2)
-
-
-def test_bytesify():
- assert bytesify(b"123") == b"123"
- assert bytesify(bytearray(b"123")) == b"123"
- assert bytesify("123") == b"123"
-
- with pytest.raises(UnicodeEncodeError):
- bytesify("\u1234")
-
- with pytest.raises(TypeError):
- bytesify(10)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/INSTALLER b/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/INSTALLER
deleted file mode 100644
index a1b589e3..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/LICENSE.md b/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/LICENSE.md
deleted file mode 100644
index 311b2b56..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/LICENSE.md
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright © 2020, [Encode OSS Ltd](https://www.encode.io/).
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-* Neither the name of the copyright holder nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/METADATA b/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/METADATA
deleted file mode 100644
index cf84d2a0..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/METADATA
+++ /dev/null
@@ -1,422 +0,0 @@
-Metadata-Version: 2.1
-Name: httpcore
-Version: 0.13.7
-Summary: A minimal low-level HTTP client.
-Home-page: https://github.com/encode/httpcore
-Author: Tom Christie
-Author-email: tom@tomchristie.com
-License: BSD
-Project-URL: Documentation, https://www.encode.io/httpcore
-Project-URL: Source, https://github.com/encode/httpcore
-Platform: UNKNOWN
-Classifier: Development Status :: 3 - Alpha
-Classifier: Environment :: Web Environment
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: BSD License
-Classifier: Operating System :: OS Independent
-Classifier: Topic :: Internet :: WWW/HTTP
-Classifier: Framework :: AsyncIO
-Classifier: Framework :: Trio
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: 3.10
-Classifier: Programming Language :: Python :: 3 :: Only
-Requires-Python: >=3.6
-Description-Content-Type: text/markdown
-License-File: LICENSE.md
-Requires-Dist: h11 (<0.13,>=0.11)
-Requires-Dist: sniffio (==1.*)
-Requires-Dist: anyio (==3.*)
-Provides-Extra: http2
-Requires-Dist: h2 (<5,>=3) ; extra == 'http2'
-
-# HTTP Core
-
-[](https://github.com/encode/httpcore/actions)
-[](https://pypi.org/project/httpcore/)
-
-> *Do one thing, and do it well.*
-
-The HTTP Core package provides a minimal low-level HTTP client, which does
-one thing only. Sending HTTP requests.
-
-It does not provide any high level model abstractions over the API,
-does not handle redirects, multipart uploads, building authentication headers,
-transparent HTTP caching, URL parsing, session cookie handling,
-content or charset decoding, handling JSON, environment based configuration
-defaults, or any of that Jazz.
-
-Some things HTTP Core does do:
-
-* Sending HTTP requests.
-* Provides both sync and async interfaces.
-* Supports HTTP/1.1 and HTTP/2.
-* Async backend support for `asyncio`, `trio` and `curio`.
-* Automatic connection pooling.
-* HTTP(S) proxy support.
-
-## Installation
-
-For HTTP/1.1 only support, install with...
-
-```shell
-$ pip install httpcore
-```
-
-For HTTP/1.1 and HTTP/2 support, install with...
-
-```shell
-$ pip install httpcore[http2]
-```
-
-## Quickstart
-
-Here's an example of making an HTTP GET request using `httpcore`...
-
-```python
-with httpcore.SyncConnectionPool() as http:
- status_code, headers, stream, extensions = http.handle_request(
- method=b'GET',
- url=(b'https', b'example.org', 443, b'/'),
- headers=[(b'host', b'example.org'), (b'user-agent', b'httpcore')],
- stream=httpcore.ByteStream(b''),
- extensions={}
- )
- body = stream.read()
- print(status_code, body)
-```
-
-Or, using async...
-
-```python
-async with httpcore.AsyncConnectionPool() as http:
- status_code, headers, stream, extensions = await http.handle_async_request(
- method=b'GET',
- url=(b'https', b'example.org', 443, b'/'),
- headers=[(b'host', b'example.org'), (b'user-agent', b'httpcore')],
- stream=httpcore.ByteStream(b''),
- extensions={}
- )
- body = await stream.aread()
- print(status_code, body)
-```
-
-## Motivation
-
-You probably don't want to be using HTTP Core directly. It might make sense if
-you're writing something like a proxy service in Python, and you just want
-something at the lowest possible level, but more typically you'll want to use
-a higher level client library, such as `httpx`.
-
-The motivation for `httpcore` is:
-
-* To provide a reusable low-level client library, that other packages can then build on top of.
-* To provide a *really clear interface split* between the networking code and client logic,
- so that each is easier to understand and reason about in isolation.
-
-
-# Changelog
-
-All notable changes to this project will be documented in this file.
-
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
-
-## 0.13.7 (September 13th, 2021)
-
-- Fix broken error messaging when URL scheme is missing, or a non HTTP(S) scheme is used. (Pull #403)
-
-## 0.13.6 (June 15th, 2021)
-
-### Fixed
-
-- Close sockets when read or write timeouts occur. (Pull #365)
-
-## 0.13.5 (June 14th, 2021)
-
-### Fixed
-
-- Resolved niggles with AnyIO EOF behaviours. (Pull #358, #362)
-
-## 0.13.4 (June 9th, 2021)
-
-### Added
-
-- Improved error messaging when URL scheme is missing, or a non HTTP(S) scheme is used. (Pull #354)
-
-### Fixed
-
-- Switched to `anyio` as the default backend implementation when running with `asyncio`. Resolves some awkward [TLS timeout issues](https://github.com/encode/httpx/discussions/1511).
-
-## 0.13.3 (May 6th, 2021)
-
-### Added
-
-- Support HTTP/2 prior knowledge, using `httpcore.SyncConnectionPool(http1=False)`. (Pull #333)
-
-### Fixed
-
-- Handle cases where environment does not provide `select.poll` support. (Pull #331)
-
-## 0.13.2 (April 29th, 2021)
-
-### Added
-
-- Improve error message for specific case of `RemoteProtocolError` where server disconnects without sending a response. (Pull #313)
-
-## 0.13.1 (April 28th, 2021)
-
-### Fixed
-
-- More resiliant testing for closed connections. (Pull #311)
-- Don't raise exceptions on ungraceful connection closes. (Pull #310)
-
-## 0.13.0 (April 21st, 2021)
-
-The 0.13 release updates the core API in order to match the HTTPX Transport API,
-introduced in HTTPX 0.18 onwards.
-
-An example of making requests with the new interface is:
-
-```python
-with httpcore.SyncConnectionPool() as http:
- status_code, headers, stream, extensions = http.handle_request(
- method=b'GET',
- url=(b'https', b'example.org', 443, b'/'),
- headers=[(b'host', b'example.org'), (b'user-agent', b'httpcore')]
- stream=httpcore.ByteStream(b''),
- extensions={}
- )
- body = stream.read()
- print(status_code, body)
-```
-
-### Changed
-
-- The `.request()` method is now `handle_request()`. (Pull #296)
-- The `.arequest()` method is now `.handle_async_request()`. (Pull #296)
-- The `headers` argument is no longer optional. (Pull #296)
-- The `stream` argument is no longer optional. (Pull #296)
-- The `ext` argument is now named `extensions`, and is no longer optional. (Pull #296)
-- The `"reason"` extension keyword is now named `"reason_phrase"`. (Pull #296)
-- The `"reason_phrase"` and `"http_version"` extensions now use byte strings for their values. (Pull #296)
-- The `httpcore.PlainByteStream()` class becomes `httpcore.ByteStream()`. (Pull #296)
-
-### Added
-
-- Streams now support a `.read()` interface. (Pull #296)
-
-### Fixed
-
-- Task cancelation no longer leaks connections from the connection pool. (Pull #305)
-
-## 0.12.3 (December 7th, 2020)
-
-### Fixed
-
-- Abort SSL connections on close rather than waiting for remote EOF when using `asyncio`. (Pull #167)
-- Fix exception raised in case of connect timeouts when using the `anyio` backend. (Pull #236)
-- Fix `Host` header precedence for `:authority` in HTTP/2. (Pull #241, #243)
-- Handle extra edge case when detecting for socket readability when using `asyncio`. (Pull #242, #244)
-- Fix `asyncio` SSL warning when using proxy tunneling. (Pull #249)
-
-## 0.12.2 (November 20th, 2020)
-
-### Fixed
-
-- Properly wrap connect errors on the asyncio backend. (Pull #235)
-- Fix `ImportError` occurring on Python 3.9 when using the HTTP/1.1 sync client in a multithreaded context. (Pull #237)
-
-## 0.12.1 (November 7th, 2020)
-
-### Added
-
-- Add connect retries. (Pull #221)
-
-### Fixed
-
-- Tweak detection of dropped connections, resolving an issue with open files limits on Linux. (Pull #185)
-- Avoid leaking connections when establishing an HTTP tunnel to a proxy has failed. (Pull #223)
-- Properly wrap OS errors when using `trio`. (Pull #225)
-
-## 0.12.0 (October 6th, 2020)
-
-### Changed
-
-- HTTP header casing is now preserved, rather than always sent in lowercase. (#216 and python-hyper/h11#104)
-
-### Added
-
-- Add Python 3.9 to officially supported versions.
-
-### Fixed
-
-- Gracefully handle a stdlib asyncio bug when a connection is closed while it is in a paused-for-reading state. (#201)
-
-## 0.11.1 (September 28nd, 2020)
-
-### Fixed
-
-- Add await to async semaphore release() coroutine (#197)
-- Drop incorrect curio classifier (#192)
-
-## 0.11.0 (September 22nd, 2020)
-
-The Transport API with 0.11.0 has a couple of significant changes.
-
-Firstly we've moved changed the request interface in order to allow extensions, which will later enable us to support features
-such as trailing headers, HTTP/2 server push, and CONNECT/Upgrade connections.
-
-The interface changes from:
-
-```python
-def request(method, url, headers, stream, timeout):
- return (http_version, status_code, reason, headers, stream)
-```
-
-To instead including an optional dictionary of extensions on the request and response:
-
-```python
-def request(method, url, headers, stream, ext):
- return (status_code, headers, stream, ext)
-```
-
-Having an open-ended extensions point will allow us to add later support for various optional features, that wouldn't otherwise be supported without these API changes.
-
-In particular:
-
-* Trailing headers support.
-* HTTP/2 Server Push
-* sendfile.
-* Exposing raw connection on CONNECT, Upgrade, HTTP/2 bi-di streaming.
-* Exposing debug information out of the API, including template name, template context.
-
-Currently extensions are limited to:
-
-* request: `timeout` - Optional. Timeout dictionary.
-* response: `http_version` - Optional. Include the HTTP version used on the response.
-* response: `reason` - Optional. Include the reason phrase used on the response. Only valid with HTTP/1.*.
-
-See https://github.com/encode/httpx/issues/1274#issuecomment-694884553 for the history behind this.
-
-Secondly, the async version of `request` is now namespaced as `arequest`.
-
-This allows concrete transports to support both sync and async implementations on the same class.
-
-### Added
-
-- Add curio support. (Pull #168)
-- Add anyio support, with `backend="anyio"`. (Pull #169)
-
-### Changed
-
-- Update the Transport API to use 'ext' for optional extensions. (Pull #190)
-- Update the Transport API to use `.request` and `.arequest` so implementations can support both sync and async. (Pull #189)
-
-## 0.10.2 (August 20th, 2020)
-
-### Added
-
-- Added Unix Domain Socket support. (Pull #139)
-
-### Fixed
-
-- Always include the port on proxy CONNECT requests. (Pull #154)
-- Fix `max_keepalive_connections` configuration. (Pull #153)
-- Fixes behaviour in HTTP/1.1 where server disconnects can be used to signal the end of the response body. (Pull #164)
-
-## 0.10.1 (August 7th, 2020)
-
-- Include `max_keepalive_connections` on `AsyncHTTPProxy`/`SyncHTTPProxy` classes.
-
-## 0.10.0 (August 7th, 2020)
-
-The most notable change in the 0.10.0 release is that HTTP/2 support is now fully optional.
-
-Use either `pip install httpcore` for HTTP/1.1 support only, or `pip install httpcore[http2]` for HTTP/1.1 and HTTP/2 support.
-
-### Added
-
-- HTTP/2 support becomes optional. (Pull #121, #130)
-- Add `local_address=...` support. (Pull #100, #134)
-- Add `PlainByteStream`, `IteratorByteStream`, `AsyncIteratorByteStream`. The `AsyncByteSteam` and `SyncByteStream` classes are now pure interface classes. (#133)
-- Add `LocalProtocolError`, `RemoteProtocolError` exceptions. (Pull #129)
-- Add `UnsupportedProtocol` exception. (Pull #128)
-- Add `.get_connection_info()` method. (Pull #102, #137)
-- Add better TRACE logs. (Pull #101)
-
-### Changed
-
-- `max_keepalive` is deprecated in favour of `max_keepalive_connections`. (Pull #140)
-
-### Fixed
-
-- Improve handling of server disconnects. (Pull #112)
-
-## 0.9.1 (May 27th, 2020)
-
-### Fixed
-
-- Proper host resolution for sync case, including IPv6 support. (Pull #97)
-- Close outstanding connections when connection pool is closed. (Pull #98)
-
-## 0.9.0 (May 21th, 2020)
-
-### Changed
-
-- URL port becomes an `Optional[int]` instead of `int`. (Pull #92)
-
-### Fixed
-
-- Honor HTTP/2 max concurrent streams settings. (Pull #89, #90)
-- Remove incorrect debug log. (Pull #83)
-
-## 0.8.4 (May 11th, 2020)
-
-### Added
-
-- Logging via HTTPCORE_LOG_LEVEL and HTTPX_LOG_LEVEL environment variables
-and TRACE level logging. (Pull #79)
-
-### Fixed
-
-- Reuse of connections on HTTP/2 in close concurrency situations. (Pull #81)
-
-## 0.8.3 (May 6rd, 2020)
-
-### Fixed
-
-- Include `Host` and `Accept` headers on proxy "CONNECT" requests.
-- De-duplicate any headers also contained in proxy_headers.
-- HTTP/2 flag not being passed down to proxy connections.
-
-## 0.8.2 (May 3rd, 2020)
-
-### Fixed
-
-- Fix connections using proxy forwarding requests not being added to the
-connection pool properly. (Pull #70)
-
-## 0.8.1 (April 30th, 2020)
-
-### Changed
-
-- Allow inherintance of both `httpcore.AsyncByteStream`, `httpcore.SyncByteStream` without type conflicts.
-
-## 0.8.0 (April 30th, 2020)
-
-### Fixed
-
-- Fixed tunnel proxy support.
-
-### Added
-
-- New `TimeoutException` base class.
-
-## 0.7.0 (March 5th, 2020)
-
-- First integration with HTTPX.
-
-
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/RECORD b/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/RECORD
deleted file mode 100644
index 653e92bc..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/RECORD
+++ /dev/null
@@ -1,67 +0,0 @@
-httpcore-0.13.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-httpcore-0.13.7.dist-info/LICENSE.md,sha256=_ctZFUx0y6uhahEkL3dAvqnyPW_rVUeRfYxflKgDkqU,1518
-httpcore-0.13.7.dist-info/METADATA,sha256=AD2A2icHFW5_CQo9WqHR3vmKaeTFXZkW2Zi_6gbFSJ8,13025
-httpcore-0.13.7.dist-info/RECORD,,
-httpcore-0.13.7.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
-httpcore-0.13.7.dist-info/top_level.txt,sha256=kYeSB6l1hBNp7JwgSwLajcsxRlrSCVKOhYKSkdgx798,59
-httpcore/__init__.py,sha256=udEv1w02RmsdoGNMPCxH1hOcZTFiEBXsnnNUoizC4Po,1656
-httpcore/__pycache__/__init__.cpython-39.pyc,,
-httpcore/__pycache__/_bytestreams.cpython-39.pyc,,
-httpcore/__pycache__/_exceptions.cpython-39.pyc,,
-httpcore/__pycache__/_threadlock.cpython-39.pyc,,
-httpcore/__pycache__/_types.cpython-39.pyc,,
-httpcore/__pycache__/_utils.cpython-39.pyc,,
-httpcore/_async/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-httpcore/_async/__pycache__/__init__.cpython-39.pyc,,
-httpcore/_async/__pycache__/base.cpython-39.pyc,,
-httpcore/_async/__pycache__/connection.cpython-39.pyc,,
-httpcore/_async/__pycache__/connection_pool.cpython-39.pyc,,
-httpcore/_async/__pycache__/http.cpython-39.pyc,,
-httpcore/_async/__pycache__/http11.cpython-39.pyc,,
-httpcore/_async/__pycache__/http2.cpython-39.pyc,,
-httpcore/_async/__pycache__/http_proxy.cpython-39.pyc,,
-httpcore/_async/base.py,sha256=uhEgVbp_560r6-80PRxK6jjV4OSuzYdbWY26K_OARC8,3264
-httpcore/_async/connection.py,sha256=ORhAgJVzI5PrQNU9w0ecsSiDsF0IuIUwKLQSkmBUajY,8350
-httpcore/_async/connection_pool.py,sha256=s5Ff430j36OL3lnJNzEHShNgMhJoQ9cSO03s11Gvl6U,13146
-httpcore/_async/http.py,sha256=6CG3ZiBXXxR-kGCpdyOWHuMTcgfp-ajPxkdAdMFf8Og,1285
-httpcore/_async/http11.py,sha256=oGrRxz4DxT6PnjP8bfLmaWvQ5NzI6OcBfUiuZZ7U078,9396
-httpcore/_async/http2.py,sha256=av5Ee5yM3hnDjiMb2paN3ObENCebCmDKfYUmPjXAtno,17082
-httpcore/_async/http_proxy.py,sha256=yDD8hXHtVHU8gLT_9VBPhgHfF0ebB6DOPlbjiuH6Viw,10004
-httpcore/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-httpcore/_backends/__pycache__/__init__.cpython-39.pyc,,
-httpcore/_backends/__pycache__/anyio.cpython-39.pyc,,
-httpcore/_backends/__pycache__/asyncio.cpython-39.pyc,,
-httpcore/_backends/__pycache__/auto.cpython-39.pyc,,
-httpcore/_backends/__pycache__/base.cpython-39.pyc,,
-httpcore/_backends/__pycache__/curio.cpython-39.pyc,,
-httpcore/_backends/__pycache__/sync.cpython-39.pyc,,
-httpcore/_backends/__pycache__/trio.cpython-39.pyc,,
-httpcore/_backends/anyio.py,sha256=OL7llxbbOv2pkzA5hjQR4mW0SLgDUEuJK0x_mD97Nu0,6317
-httpcore/_backends/asyncio.py,sha256=rg9-BCdRqD65_4EC6U0D-jMXkK4oV_PbYfPBeYptYj0,10700
-httpcore/_backends/auto.py,sha256=DhL7k6Iww7qkugkpeBzPQq4mySCCb9G_PK-w_zOqVUc,2211
-httpcore/_backends/base.py,sha256=hmAUxgADI-fmWciRs4iBxa0A2E-avawuaOWocX_A9nM,3796
-httpcore/_backends/curio.py,sha256=Zr3mfo7q8wpfkzXv3atEyAkbB-4NtndYWw56gEh7kDQ,6230
-httpcore/_backends/sync.py,sha256=W9WQq2lLOqZ1IhirZATFDDvKVWAdSJjeNja_vwZIg8E,5494
-httpcore/_backends/trio.py,sha256=nwEuP6_xIIFy6vqBs0XXxfqROk99GnDyLhiOIsJHcsQ,6818
-httpcore/_bytestreams.py,sha256=aZQvmevkf27rgnwMwumkOpzK5GBSwbe1WTTnkNvS910,2430
-httpcore/_exceptions.py,sha256=xieninAoG-IeEIma6OIjNDlUfUAYyH_Hx652U2RVKws,1115
-httpcore/_sync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-httpcore/_sync/__pycache__/__init__.cpython-39.pyc,,
-httpcore/_sync/__pycache__/base.cpython-39.pyc,,
-httpcore/_sync/__pycache__/connection.cpython-39.pyc,,
-httpcore/_sync/__pycache__/connection_pool.cpython-39.pyc,,
-httpcore/_sync/__pycache__/http.cpython-39.pyc,,
-httpcore/_sync/__pycache__/http11.cpython-39.pyc,,
-httpcore/_sync/__pycache__/http2.cpython-39.pyc,,
-httpcore/_sync/__pycache__/http_proxy.cpython-39.pyc,,
-httpcore/_sync/base.py,sha256=HeUz5H5t_WN4GDpwhz6hCsgL75JJnXwo8Jn9Ms3m1NM,3167
-httpcore/_sync/connection.py,sha256=DeE7z9ky3CyQUl9lD72O2bcawzv-zKbZ7RTq6UrRe4A,8231
-httpcore/_sync/connection_pool.py,sha256=6cUbHjaK5cfs4rWVN7F4hOxk2IxIp1C5bfVHieSINlM,12866
-httpcore/_sync/http.py,sha256=Dhcrb6AqgHyh18QFq1NysUS-6W5z6-guFMwwC6lVwAg,1274
-httpcore/_sync/http11.py,sha256=hhlEv95rfDr-vJW5OSwTvqthkGNYH9a6jc6p1RrGoJ8,9209
-httpcore/_sync/http2.py,sha256=JdLSySBTzkOnZ4KQzfaQOZYrsinHeTScJnuKBEyfGP4,16727
-httpcore/_sync/http_proxy.py,sha256=p8zuucWqny1nhP3qVPmGdUwUF8jNq2Yf-IM6S5Bf-QE,9869
-httpcore/_threadlock.py,sha256=Xc-WeI8tDh2Ivt7Chblv3HmhbBgZXKMo5SMneXjZDCE,813
-httpcore/_types.py,sha256=97NJ04exPaPoYZB_y4eV4qYfqeyr9XE-zYqkGEAaGuI,331
-httpcore/_utils.py,sha256=goElgq6cnQR0HSJI32taOi-gAJKO3Lr_kCJ0VHPv-XM,3691
-httpcore/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/WHEEL b/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/WHEEL
deleted file mode 100644
index 5bad85fd..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/WHEEL
+++ /dev/null
@@ -1,5 +0,0 @@
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.37.0)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/top_level.txt b/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/top_level.txt
deleted file mode 100644
index 613e4350..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore-0.13.7.dist-info/top_level.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-httpcore
-httpcore/_async
-httpcore/_backends
-httpcore/_sync
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__init__.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/__init__.py
deleted file mode 100644
index 3ddc6d61..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__init__.py
+++ /dev/null
@@ -1,63 +0,0 @@
-from ._async.base import AsyncByteStream, AsyncHTTPTransport
-from ._async.connection_pool import AsyncConnectionPool
-from ._async.http_proxy import AsyncHTTPProxy
-from ._bytestreams import AsyncIteratorByteStream, ByteStream, IteratorByteStream
-from ._exceptions import (
- CloseError,
- ConnectError,
- ConnectTimeout,
- LocalProtocolError,
- NetworkError,
- PoolTimeout,
- ProtocolError,
- ProxyError,
- ReadError,
- ReadTimeout,
- RemoteProtocolError,
- TimeoutException,
- UnsupportedProtocol,
- WriteError,
- WriteTimeout,
-)
-from ._sync.base import SyncByteStream, SyncHTTPTransport
-from ._sync.connection_pool import SyncConnectionPool
-from ._sync.http_proxy import SyncHTTPProxy
-
-__all__ = [
- "AsyncByteStream",
- "AsyncConnectionPool",
- "AsyncHTTPProxy",
- "AsyncHTTPTransport",
- "AsyncIteratorByteStream",
- "ByteStream",
- "CloseError",
- "ConnectError",
- "ConnectTimeout",
- "IteratorByteStream",
- "LocalProtocolError",
- "NetworkError",
- "PoolTimeout",
- "ProtocolError",
- "ProxyError",
- "ReadError",
- "ReadTimeout",
- "RemoteProtocolError",
- "SyncByteStream",
- "SyncConnectionPool",
- "SyncHTTPProxy",
- "SyncHTTPTransport",
- "TimeoutException",
- "UnsupportedProtocol",
- "WriteError",
- "WriteTimeout",
-]
-__version__ = "0.13.7"
-
-__locals = locals()
-
-for _name in __all__:
- if not _name.startswith("__"):
- # Save original source module, used by Sphinx.
- __locals[_name].__source_module__ = __locals[_name].__module__
- # Override module for prettier repr().
- setattr(__locals[_name], "__module__", "httpcore") # noqa
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/__init__.cpython-39.pyc
deleted file mode 100644
index 89af5f5e..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/__init__.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_bytestreams.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_bytestreams.cpython-39.pyc
deleted file mode 100644
index 92380230..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_bytestreams.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_exceptions.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_exceptions.cpython-39.pyc
deleted file mode 100644
index ada1076d..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_exceptions.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_threadlock.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_threadlock.cpython-39.pyc
deleted file mode 100644
index 200bc12a..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_threadlock.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_types.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_types.cpython-39.pyc
deleted file mode 100644
index 9e57a90e..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_types.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_utils.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_utils.cpython-39.pyc
deleted file mode 100644
index 34b6d39d..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/__pycache__/_utils.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__init__.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/__init__.cpython-39.pyc
deleted file mode 100644
index 1bd7d17d..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/__init__.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/base.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/base.cpython-39.pyc
deleted file mode 100644
index 8bdf6866..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/base.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection.cpython-39.pyc
deleted file mode 100644
index e7339e45..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-39.pyc
deleted file mode 100644
index ef8f3d1d..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/connection_pool.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/http.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/http.cpython-39.pyc
deleted file mode 100644
index c1032829..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/http.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/http11.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/http11.cpython-39.pyc
deleted file mode 100644
index 6860de2a..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/http11.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/http2.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/http2.cpython-39.pyc
deleted file mode 100644
index a81d71d4..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/http2.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-39.pyc
deleted file mode 100644
index 3e2b6df6..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/__pycache__/http_proxy.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/base.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/base.py
deleted file mode 100644
index 2b3961c2..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/base.py
+++ /dev/null
@@ -1,122 +0,0 @@
-import enum
-from types import TracebackType
-from typing import AsyncIterator, Tuple, Type
-
-from .._types import URL, Headers, T
-
-
-class NewConnectionRequired(Exception):
- pass
-
-
-class ConnectionState(enum.IntEnum):
- """
- PENDING READY
- | | ^
- v V |
- ACTIVE |
- | | |
- | V |
- V IDLE-+
- FULL |
- | |
- V V
- CLOSED
- """
-
- PENDING = 0 # Connection not yet acquired.
- READY = 1 # Re-acquired from pool, about to send a request.
- ACTIVE = 2 # Active requests.
- FULL = 3 # Active requests, no more stream IDs available.
- IDLE = 4 # No active requests.
- CLOSED = 5 # Connection closed.
-
-
-class AsyncByteStream:
- """
- The base interface for request and response bodies.
-
- Concrete implementations should subclass this class, and implement
- the :meth:`__aiter__` method, and optionally the :meth:`aclose` method.
- """
-
- async def __aiter__(self) -> AsyncIterator[bytes]:
- """
- Yield bytes representing the request or response body.
- """
- yield b"" # pragma: nocover
-
- async def aclose(self) -> None:
- """
- Must be called by the client to indicate that the stream has been closed.
- """
- pass # pragma: nocover
-
- async def aread(self) -> bytes:
- try:
- return b"".join([part async for part in self])
- finally:
- await self.aclose()
-
-
-class AsyncHTTPTransport:
- """
- The base interface for sending HTTP requests.
-
- Concrete implementations should subclass this class, and implement
- the :meth:`handle_async_request` method, and optionally the :meth:`aclose` method.
- """
-
- async def handle_async_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: AsyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, AsyncByteStream, dict]:
- """
- The interface for sending a single HTTP request, and returning a response.
-
- Parameters
- ----------
- method:
- The HTTP method, such as ``b'GET'``.
- url:
- The URL as a 4-tuple of (scheme, host, port, path).
- headers:
- Any HTTP headers to send with the request.
- stream:
- The body of the HTTP request.
- extensions:
- A dictionary of optional extensions.
-
- Returns
- -------
- status_code:
- The HTTP status code, such as ``200``.
- headers:
- Any HTTP headers included on the response.
- stream:
- The body of the HTTP response.
- extensions:
- A dictionary of optional extensions.
- """
- raise NotImplementedError() # pragma: nocover
-
- async def aclose(self) -> None:
- """
- Close the implementation, which should close any outstanding response streams,
- and any keep alive connections.
- """
-
- async def __aenter__(self: T) -> T:
- return self
-
- async def __aexit__(
- self,
- exc_type: Type[BaseException] = None,
- exc_value: BaseException = None,
- traceback: TracebackType = None,
- ) -> None:
- await self.aclose()
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/connection.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/connection.py
deleted file mode 100644
index 2add4d85..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/connection.py
+++ /dev/null
@@ -1,220 +0,0 @@
-from ssl import SSLContext
-from typing import List, Optional, Tuple, cast
-
-from .._backends.auto import AsyncBackend, AsyncLock, AsyncSocketStream, AutoBackend
-from .._exceptions import ConnectError, ConnectTimeout
-from .._types import URL, Headers, Origin, TimeoutDict
-from .._utils import exponential_backoff, get_logger, url_to_origin
-from .base import AsyncByteStream, AsyncHTTPTransport, NewConnectionRequired
-from .http import AsyncBaseHTTPConnection
-from .http11 import AsyncHTTP11Connection
-
-logger = get_logger(__name__)
-
-RETRIES_BACKOFF_FACTOR = 0.5 # 0s, 0.5s, 1s, 2s, 4s, etc.
-
-
-class AsyncHTTPConnection(AsyncHTTPTransport):
- def __init__(
- self,
- origin: Origin,
- http1: bool = True,
- http2: bool = False,
- keepalive_expiry: float = None,
- uds: str = None,
- ssl_context: SSLContext = None,
- socket: AsyncSocketStream = None,
- local_address: str = None,
- retries: int = 0,
- backend: AsyncBackend = None,
- ):
- self.origin = origin
- self._http1_enabled = http1
- self._http2_enabled = http2
- self._keepalive_expiry = keepalive_expiry
- self._uds = uds
- self._ssl_context = SSLContext() if ssl_context is None else ssl_context
- self.socket = socket
- self._local_address = local_address
- self._retries = retries
-
- alpn_protocols: List[str] = []
- if http1:
- alpn_protocols.append("http/1.1")
- if http2:
- alpn_protocols.append("h2")
-
- self._ssl_context.set_alpn_protocols(alpn_protocols)
-
- self.connection: Optional[AsyncBaseHTTPConnection] = None
- self._is_http11 = False
- self._is_http2 = False
- self._connect_failed = False
- self._expires_at: Optional[float] = None
- self._backend = AutoBackend() if backend is None else backend
-
- def __repr__(self) -> str:
- return f""
-
- def info(self) -> str:
- if self.connection is None:
- return "Connection failed" if self._connect_failed else "Connecting"
- return self.connection.info()
-
- def should_close(self) -> bool:
- """
- Return `True` if the connection is in a state where it should be closed.
- This occurs when any of the following occur:
-
- * There are no active requests on an HTTP/1.1 connection, and the underlying
- socket is readable. The only valid state the socket can be readable in
- if this occurs is when the b"" EOF marker is about to be returned,
- indicating a server disconnect.
- * There are no active requests being made and the keepalive timeout has passed.
- """
- if self.connection is None:
- return False
- return self.connection.should_close()
-
- def is_idle(self) -> bool:
- """
- Return `True` if the connection is currently idle.
- """
- if self.connection is None:
- return False
- return self.connection.is_idle()
-
- def is_closed(self) -> bool:
- if self.connection is None:
- return self._connect_failed
- return self.connection.is_closed()
-
- def is_available(self) -> bool:
- """
- Return `True` if the connection is currently able to accept an outgoing request.
- This occurs when any of the following occur:
-
- * The connection has not yet been opened, and HTTP/2 support is enabled.
- We don't *know* at this point if we'll end up on an HTTP/2 connection or
- not, but we *might* do, so we indicate availability.
- * The connection has been opened, and is currently idle.
- * The connection is open, and is an HTTP/2 connection. The connection must
- also not currently be exceeding the maximum number of allowable concurrent
- streams and must not have exhausted the maximum total number of stream IDs.
- """
- if self.connection is None:
- return self._http2_enabled and not self.is_closed
- return self.connection.is_available()
-
- @property
- def request_lock(self) -> AsyncLock:
- # We do this lazily, to make sure backend autodetection always
- # runs within an async context.
- if not hasattr(self, "_request_lock"):
- self._request_lock = self._backend.create_lock()
- return self._request_lock
-
- async def handle_async_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: AsyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, AsyncByteStream, dict]:
- assert url_to_origin(url) == self.origin
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
-
- async with self.request_lock:
- if self.connection is None:
- if self._connect_failed:
- raise NewConnectionRequired()
- if not self.socket:
- logger.trace(
- "open_socket origin=%r timeout=%r", self.origin, timeout
- )
- self.socket = await self._open_socket(timeout)
- self._create_connection(self.socket)
- elif not self.connection.is_available():
- raise NewConnectionRequired()
-
- assert self.connection is not None
- logger.trace(
- "connection.handle_async_request method=%r url=%r headers=%r",
- method,
- url,
- headers,
- )
- return await self.connection.handle_async_request(
- method, url, headers, stream, extensions
- )
-
- async def _open_socket(self, timeout: TimeoutDict = None) -> AsyncSocketStream:
- scheme, hostname, port = self.origin
- timeout = {} if timeout is None else timeout
- ssl_context = self._ssl_context if scheme == b"https" else None
-
- retries_left = self._retries
- delays = exponential_backoff(factor=RETRIES_BACKOFF_FACTOR)
-
- while True:
- try:
- if self._uds is None:
- return await self._backend.open_tcp_stream(
- hostname,
- port,
- ssl_context,
- timeout,
- local_address=self._local_address,
- )
- else:
- return await self._backend.open_uds_stream(
- self._uds, hostname, ssl_context, timeout
- )
- except (ConnectError, ConnectTimeout):
- if retries_left <= 0:
- self._connect_failed = True
- raise
- retries_left -= 1
- delay = next(delays)
- await self._backend.sleep(delay)
- except Exception: # noqa: PIE786
- self._connect_failed = True
- raise
-
- def _create_connection(self, socket: AsyncSocketStream) -> None:
- http_version = socket.get_http_version()
- logger.trace(
- "create_connection socket=%r http_version=%r", socket, http_version
- )
- if http_version == "HTTP/2" or (
- self._http2_enabled and not self._http1_enabled
- ):
- from .http2 import AsyncHTTP2Connection
-
- self._is_http2 = True
- self.connection = AsyncHTTP2Connection(
- socket=socket,
- keepalive_expiry=self._keepalive_expiry,
- backend=self._backend,
- )
- else:
- self._is_http11 = True
- self.connection = AsyncHTTP11Connection(
- socket=socket, keepalive_expiry=self._keepalive_expiry
- )
-
- async def start_tls(
- self, hostname: bytes, ssl_context: SSLContext, timeout: TimeoutDict = None
- ) -> None:
- if self.connection is not None:
- logger.trace("start_tls hostname=%r timeout=%r", hostname, timeout)
- self.socket = await self.connection.start_tls(
- hostname, ssl_context, timeout
- )
- logger.trace("start_tls complete hostname=%r timeout=%r", hostname, timeout)
-
- async def aclose(self) -> None:
- async with self.request_lock:
- if self.connection is not None:
- await self.connection.aclose()
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/connection_pool.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/connection_pool.py
deleted file mode 100644
index 0902ac2f..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/connection_pool.py
+++ /dev/null
@@ -1,362 +0,0 @@
-import warnings
-from ssl import SSLContext
-from typing import (
- AsyncIterator,
- Callable,
- Dict,
- List,
- Optional,
- Set,
- Tuple,
- Union,
- cast,
-)
-
-from .._backends.auto import AsyncBackend, AsyncLock, AsyncSemaphore
-from .._backends.base import lookup_async_backend
-from .._exceptions import LocalProtocolError, PoolTimeout, UnsupportedProtocol
-from .._threadlock import ThreadLock
-from .._types import URL, Headers, Origin, TimeoutDict
-from .._utils import get_logger, origin_to_url_string, url_to_origin
-from .base import AsyncByteStream, AsyncHTTPTransport, NewConnectionRequired
-from .connection import AsyncHTTPConnection
-
-logger = get_logger(__name__)
-
-
-class NullSemaphore(AsyncSemaphore):
- def __init__(self) -> None:
- pass
-
- async def acquire(self, timeout: float = None) -> None:
- return
-
- async def release(self) -> None:
- return
-
-
-class ResponseByteStream(AsyncByteStream):
- def __init__(
- self,
- stream: AsyncByteStream,
- connection: AsyncHTTPConnection,
- callback: Callable,
- ) -> None:
- """
- A wrapper around the response stream that we return from
- `.handle_async_request()`.
-
- Ensures that when `stream.aclose()` is called, the connection pool
- is notified via a callback.
- """
- self.stream = stream
- self.connection = connection
- self.callback = callback
-
- async def __aiter__(self) -> AsyncIterator[bytes]:
- async for chunk in self.stream:
- yield chunk
-
- async def aclose(self) -> None:
- try:
- # Call the underlying stream close callback.
- # This will be a call to `AsyncHTTP11Connection._response_closed()`
- # or `AsyncHTTP2Stream._response_closed()`.
- await self.stream.aclose()
- finally:
- # Call the connection pool close callback.
- # This will be a call to `AsyncConnectionPool._response_closed()`.
- await self.callback(self.connection)
-
-
-class AsyncConnectionPool(AsyncHTTPTransport):
- """
- A connection pool for making HTTP requests.
-
- Parameters
- ----------
- ssl_context:
- An SSL context to use for verifying connections.
- max_connections:
- The maximum number of concurrent connections to allow.
- max_keepalive_connections:
- The maximum number of connections to allow before closing keep-alive
- connections.
- keepalive_expiry:
- The maximum time to allow before closing a keep-alive connection.
- http1:
- Enable/Disable HTTP/1.1 support. Defaults to True.
- http2:
- Enable/Disable HTTP/2 support. Defaults to False.
- uds:
- Path to a Unix Domain Socket to use instead of TCP sockets.
- local_address:
- Local address to connect from. Can also be used to connect using a particular
- address family. Using ``local_address="0.0.0.0"`` will connect using an
- ``AF_INET`` address (IPv4), while using ``local_address="::"`` will connect
- using an ``AF_INET6`` address (IPv6).
- retries:
- The maximum number of retries when trying to establish a connection.
- backend:
- A name indicating which concurrency backend to use.
- """
-
- def __init__(
- self,
- ssl_context: SSLContext = None,
- max_connections: int = None,
- max_keepalive_connections: int = None,
- keepalive_expiry: float = None,
- http1: bool = True,
- http2: bool = False,
- uds: str = None,
- local_address: str = None,
- retries: int = 0,
- max_keepalive: int = None,
- backend: Union[AsyncBackend, str] = "auto",
- ):
- if max_keepalive is not None:
- warnings.warn(
- "'max_keepalive' is deprecated. Use 'max_keepalive_connections'.",
- DeprecationWarning,
- )
- max_keepalive_connections = max_keepalive
-
- if isinstance(backend, str):
- backend = lookup_async_backend(backend)
-
- self._ssl_context = SSLContext() if ssl_context is None else ssl_context
- self._max_connections = max_connections
- self._max_keepalive_connections = max_keepalive_connections
- self._keepalive_expiry = keepalive_expiry
- self._http1 = http1
- self._http2 = http2
- self._uds = uds
- self._local_address = local_address
- self._retries = retries
- self._connections: Dict[Origin, Set[AsyncHTTPConnection]] = {}
- self._thread_lock = ThreadLock()
- self._backend = backend
- self._next_keepalive_check = 0.0
-
- if not (http1 or http2):
- raise ValueError("Either http1 or http2 must be True.")
-
- if http2:
- try:
- import h2 # noqa: F401
- except ImportError:
- raise ImportError(
- "Attempted to use http2=True, but the 'h2' "
- "package is not installed. Use 'pip install httpcore[http2]'."
- )
-
- @property
- def _connection_semaphore(self) -> AsyncSemaphore:
- # We do this lazily, to make sure backend autodetection always
- # runs within an async context.
- if not hasattr(self, "_internal_semaphore"):
- if self._max_connections is not None:
- self._internal_semaphore = self._backend.create_semaphore(
- self._max_connections, exc_class=PoolTimeout
- )
- else:
- self._internal_semaphore = NullSemaphore()
-
- return self._internal_semaphore
-
- @property
- def _connection_acquiry_lock(self) -> AsyncLock:
- if not hasattr(self, "_internal_connection_acquiry_lock"):
- self._internal_connection_acquiry_lock = self._backend.create_lock()
- return self._internal_connection_acquiry_lock
-
- def _create_connection(
- self,
- origin: Tuple[bytes, bytes, int],
- ) -> AsyncHTTPConnection:
- return AsyncHTTPConnection(
- origin=origin,
- http1=self._http1,
- http2=self._http2,
- keepalive_expiry=self._keepalive_expiry,
- uds=self._uds,
- ssl_context=self._ssl_context,
- local_address=self._local_address,
- retries=self._retries,
- backend=self._backend,
- )
-
- async def handle_async_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: AsyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, AsyncByteStream, dict]:
- if not url[0]:
- raise UnsupportedProtocol(
- "Request URL missing either an 'http://' or 'https://' protocol."
- )
-
- if url[0] not in (b"http", b"https"):
- protocol = url[0].decode("ascii")
- raise UnsupportedProtocol(
- f"Request URL has an unsupported protocol '{protocol}://'."
- )
-
- if not url[1]:
- raise LocalProtocolError("Missing hostname in URL.")
-
- origin = url_to_origin(url)
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
-
- await self._keepalive_sweep()
-
- connection: Optional[AsyncHTTPConnection] = None
- while connection is None:
- async with self._connection_acquiry_lock:
- # We get-or-create a connection as an atomic operation, to ensure
- # that HTTP/2 requests issued in close concurrency will end up
- # on the same connection.
- logger.trace("get_connection_from_pool=%r", origin)
- connection = await self._get_connection_from_pool(origin)
-
- if connection is None:
- connection = self._create_connection(origin=origin)
- logger.trace("created connection=%r", connection)
- await self._add_to_pool(connection, timeout=timeout)
- else:
- logger.trace("reuse connection=%r", connection)
-
- try:
- response = await connection.handle_async_request(
- method, url, headers=headers, stream=stream, extensions=extensions
- )
- except NewConnectionRequired:
- connection = None
- except BaseException: # noqa: PIE786
- # See https://github.com/encode/httpcore/pull/305 for motivation
- # behind catching 'BaseException' rather than 'Exception' here.
- logger.trace("remove from pool connection=%r", connection)
- await self._remove_from_pool(connection)
- raise
-
- status_code, headers, stream, extensions = response
- wrapped_stream = ResponseByteStream(
- stream, connection=connection, callback=self._response_closed
- )
- return status_code, headers, wrapped_stream, extensions
-
- async def _get_connection_from_pool(
- self, origin: Origin
- ) -> Optional[AsyncHTTPConnection]:
- # Determine expired keep alive connections on this origin.
- reuse_connection = None
- connections_to_close = set()
-
- for connection in self._connections_for_origin(origin):
- if connection.should_close():
- connections_to_close.add(connection)
- await self._remove_from_pool(connection)
- elif connection.is_available():
- reuse_connection = connection
-
- # Close any dropped connections.
- for connection in connections_to_close:
- await connection.aclose()
-
- return reuse_connection
-
- async def _response_closed(self, connection: AsyncHTTPConnection) -> None:
- remove_from_pool = False
- close_connection = False
-
- if connection.is_closed():
- remove_from_pool = True
- elif connection.is_idle():
- num_connections = len(self._get_all_connections())
- if (
- self._max_keepalive_connections is not None
- and num_connections > self._max_keepalive_connections
- ):
- remove_from_pool = True
- close_connection = True
-
- if remove_from_pool:
- await self._remove_from_pool(connection)
-
- if close_connection:
- await connection.aclose()
-
- async def _keepalive_sweep(self) -> None:
- """
- Remove any IDLE connections that have expired past their keep-alive time.
- """
- if self._keepalive_expiry is None:
- return
-
- now = await self._backend.time()
- if now < self._next_keepalive_check:
- return
-
- self._next_keepalive_check = now + min(1.0, self._keepalive_expiry)
- connections_to_close = set()
-
- for connection in self._get_all_connections():
- if connection.should_close():
- connections_to_close.add(connection)
- await self._remove_from_pool(connection)
-
- for connection in connections_to_close:
- await connection.aclose()
-
- async def _add_to_pool(
- self, connection: AsyncHTTPConnection, timeout: TimeoutDict
- ) -> None:
- logger.trace("adding connection to pool=%r", connection)
- await self._connection_semaphore.acquire(timeout=timeout.get("pool", None))
- async with self._thread_lock:
- self._connections.setdefault(connection.origin, set())
- self._connections[connection.origin].add(connection)
-
- async def _remove_from_pool(self, connection: AsyncHTTPConnection) -> None:
- logger.trace("removing connection from pool=%r", connection)
- async with self._thread_lock:
- if connection in self._connections.get(connection.origin, set()):
- await self._connection_semaphore.release()
- self._connections[connection.origin].remove(connection)
- if not self._connections[connection.origin]:
- del self._connections[connection.origin]
-
- def _connections_for_origin(self, origin: Origin) -> Set[AsyncHTTPConnection]:
- return set(self._connections.get(origin, set()))
-
- def _get_all_connections(self) -> Set[AsyncHTTPConnection]:
- connections: Set[AsyncHTTPConnection] = set()
- for connection_set in self._connections.values():
- connections |= connection_set
- return connections
-
- async def aclose(self) -> None:
- connections = self._get_all_connections()
- for connection in connections:
- await self._remove_from_pool(connection)
-
- # Close all connections
- for connection in connections:
- await connection.aclose()
-
- async def get_connection_info(self) -> Dict[str, List[str]]:
- """
- Returns a dict of origin URLs to a list of summary strings for each connection.
- """
- await self._keepalive_sweep()
-
- stats = {}
- for origin, connections in self._connections.items():
- stats[origin_to_url_string(origin)] = sorted(
- [connection.info() for connection in connections]
- )
- return stats
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/http.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/http.py
deleted file mode 100644
index 06270f0f..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/http.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from ssl import SSLContext
-
-from .._backends.auto import AsyncSocketStream
-from .._types import TimeoutDict
-from .base import AsyncHTTPTransport
-
-
-class AsyncBaseHTTPConnection(AsyncHTTPTransport):
- def info(self) -> str:
- raise NotImplementedError() # pragma: nocover
-
- def should_close(self) -> bool:
- """
- Return `True` if the connection is in a state where it should be closed.
- """
- raise NotImplementedError() # pragma: nocover
-
- def is_idle(self) -> bool:
- """
- Return `True` if the connection is currently idle.
- """
- raise NotImplementedError() # pragma: nocover
-
- def is_closed(self) -> bool:
- """
- Return `True` if the connection has been closed.
- """
- raise NotImplementedError() # pragma: nocover
-
- def is_available(self) -> bool:
- """
- Return `True` if the connection is currently able to accept an outgoing request.
- """
- raise NotImplementedError() # pragma: nocover
-
- async def start_tls(
- self, hostname: bytes, ssl_context: SSLContext, timeout: TimeoutDict = None
- ) -> AsyncSocketStream:
- """
- Upgrade the underlying socket to TLS.
- """
- raise NotImplementedError() # pragma: nocover
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/http11.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/http11.py
deleted file mode 100644
index a265657c..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/http11.py
+++ /dev/null
@@ -1,269 +0,0 @@
-import enum
-import time
-from ssl import SSLContext
-from typing import AsyncIterator, List, Optional, Tuple, Union, cast
-
-import h11
-
-from .._backends.auto import AsyncSocketStream
-from .._bytestreams import AsyncIteratorByteStream
-from .._exceptions import LocalProtocolError, RemoteProtocolError, map_exceptions
-from .._types import URL, Headers, TimeoutDict
-from .._utils import get_logger
-from .base import AsyncByteStream, NewConnectionRequired
-from .http import AsyncBaseHTTPConnection
-
-H11Event = Union[
- h11.Request,
- h11.Response,
- h11.InformationalResponse,
- h11.Data,
- h11.EndOfMessage,
- h11.ConnectionClosed,
-]
-
-
-class ConnectionState(enum.IntEnum):
- NEW = 0
- ACTIVE = 1
- IDLE = 2
- CLOSED = 3
-
-
-logger = get_logger(__name__)
-
-
-class AsyncHTTP11Connection(AsyncBaseHTTPConnection):
- READ_NUM_BYTES = 64 * 1024
-
- def __init__(self, socket: AsyncSocketStream, keepalive_expiry: float = None):
- self.socket = socket
-
- self._keepalive_expiry: Optional[float] = keepalive_expiry
- self._should_expire_at: Optional[float] = None
- self._h11_state = h11.Connection(our_role=h11.CLIENT)
- self._state = ConnectionState.NEW
-
- def __repr__(self) -> str:
- return f""
-
- def _now(self) -> float:
- return time.monotonic()
-
- def _server_disconnected(self) -> bool:
- """
- Return True if the connection is idle, and the underlying socket is readable.
- The only valid state the socket can be readable here is when the b""
- EOF marker is about to be returned, indicating a server disconnect.
- """
- return self._state == ConnectionState.IDLE and self.socket.is_readable()
-
- def _keepalive_expired(self) -> bool:
- """
- Return True if the connection is idle, and has passed it's keepalive
- expiry time.
- """
- return (
- self._state == ConnectionState.IDLE
- and self._should_expire_at is not None
- and self._now() >= self._should_expire_at
- )
-
- def info(self) -> str:
- return f"HTTP/1.1, {self._state.name}"
-
- def should_close(self) -> bool:
- """
- Return `True` if the connection is in a state where it should be closed.
- """
- return self._server_disconnected() or self._keepalive_expired()
-
- def is_idle(self) -> bool:
- """
- Return `True` if the connection is currently idle.
- """
- return self._state == ConnectionState.IDLE
-
- def is_closed(self) -> bool:
- """
- Return `True` if the connection has been closed.
- """
- return self._state == ConnectionState.CLOSED
-
- def is_available(self) -> bool:
- """
- Return `True` if the connection is currently able to accept an outgoing request.
- """
- return self._state == ConnectionState.IDLE
-
- async def handle_async_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: AsyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, AsyncByteStream, dict]:
- """
- Send a single HTTP/1.1 request.
-
- Note that there is no kind of task/thread locking at this layer of interface.
- Dealing with locking for concurrency is handled by the `AsyncHTTPConnection`.
- """
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
-
- if self._state in (ConnectionState.NEW, ConnectionState.IDLE):
- self._state = ConnectionState.ACTIVE
- self._should_expire_at = None
- else:
- raise NewConnectionRequired()
-
- await self._send_request(method, url, headers, timeout)
- await self._send_request_body(stream, timeout)
- (
- http_version,
- status_code,
- reason_phrase,
- headers,
- ) = await self._receive_response(timeout)
- response_stream = AsyncIteratorByteStream(
- aiterator=self._receive_response_data(timeout),
- aclose_func=self._response_closed,
- )
- extensions = {
- "http_version": http_version,
- "reason_phrase": reason_phrase,
- }
- return (status_code, headers, response_stream, extensions)
-
- async def start_tls(
- self, hostname: bytes, ssl_context: SSLContext, timeout: TimeoutDict = None
- ) -> AsyncSocketStream:
- timeout = {} if timeout is None else timeout
- self.socket = await self.socket.start_tls(hostname, ssl_context, timeout)
- return self.socket
-
- async def _send_request(
- self, method: bytes, url: URL, headers: Headers, timeout: TimeoutDict
- ) -> None:
- """
- Send the request line and headers.
- """
- logger.trace("send_request method=%r url=%r headers=%s", method, url, headers)
- _scheme, _host, _port, target = url
- with map_exceptions({h11.LocalProtocolError: LocalProtocolError}):
- event = h11.Request(method=method, target=target, headers=headers)
- await self._send_event(event, timeout)
-
- async def _send_request_body(
- self, stream: AsyncByteStream, timeout: TimeoutDict
- ) -> None:
- """
- Send the request body.
- """
- # Send the request body.
- async for chunk in stream:
- logger.trace("send_data=Data(<%d bytes>)", len(chunk))
- event = h11.Data(data=chunk)
- await self._send_event(event, timeout)
-
- # Finalize sending the request.
- event = h11.EndOfMessage()
- await self._send_event(event, timeout)
-
- async def _send_event(self, event: H11Event, timeout: TimeoutDict) -> None:
- """
- Send a single `h11` event to the network, waiting for the data to
- drain before returning.
- """
- bytes_to_send = self._h11_state.send(event)
- await self.socket.write(bytes_to_send, timeout)
-
- async def _receive_response(
- self, timeout: TimeoutDict
- ) -> Tuple[bytes, int, bytes, List[Tuple[bytes, bytes]]]:
- """
- Read the response status and headers from the network.
- """
- while True:
- event = await self._receive_event(timeout)
- if isinstance(event, h11.Response):
- break
-
- http_version = b"HTTP/" + event.http_version
-
- # h11 version 0.11+ supports a `raw_items` interface to get the
- # raw header casing, rather than the enforced lowercase headers.
- headers = event.headers.raw_items()
-
- return http_version, event.status_code, event.reason, headers
-
- async def _receive_response_data(
- self, timeout: TimeoutDict
- ) -> AsyncIterator[bytes]:
- """
- Read the response data from the network.
- """
- while True:
- event = await self._receive_event(timeout)
- if isinstance(event, h11.Data):
- logger.trace("receive_event=Data(<%d bytes>)", len(event.data))
- yield bytes(event.data)
- elif isinstance(event, (h11.EndOfMessage, h11.PAUSED)):
- logger.trace("receive_event=%r", event)
- break
-
- async def _receive_event(self, timeout: TimeoutDict) -> H11Event:
- """
- Read a single `h11` event, reading more data from the network if needed.
- """
- while True:
- with map_exceptions({h11.RemoteProtocolError: RemoteProtocolError}):
- event = self._h11_state.next_event()
-
- if event is h11.NEED_DATA:
- data = await self.socket.read(self.READ_NUM_BYTES, timeout)
-
- # If we feed this case through h11 we'll raise an exception like:
- #
- # httpcore.RemoteProtocolError: can't handle event type
- # ConnectionClosed when role=SERVER and state=SEND_RESPONSE
- #
- # Which is accurate, but not very informative from an end-user
- # perspective. Instead we handle messaging for this case distinctly.
- if data == b"" and self._h11_state.their_state == h11.SEND_RESPONSE:
- msg = "Server disconnected without sending a response."
- raise RemoteProtocolError(msg)
-
- self._h11_state.receive_data(data)
- else:
- assert event is not h11.NEED_DATA
- break
- return event
-
- async def _response_closed(self) -> None:
- logger.trace(
- "response_closed our_state=%r their_state=%r",
- self._h11_state.our_state,
- self._h11_state.their_state,
- )
- if (
- self._h11_state.our_state is h11.DONE
- and self._h11_state.their_state is h11.DONE
- ):
- self._h11_state.start_next_cycle()
- self._state = ConnectionState.IDLE
- if self._keepalive_expiry is not None:
- self._should_expire_at = self._now() + self._keepalive_expiry
- else:
- await self.aclose()
-
- async def aclose(self) -> None:
- if self._state != ConnectionState.CLOSED:
- self._state = ConnectionState.CLOSED
-
- if self._h11_state.our_state is h11.MUST_CLOSE:
- event = h11.ConnectionClosed()
- self._h11_state.send(event)
-
- await self.socket.aclose()
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/http2.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/http2.py
deleted file mode 100644
index 35a4e091..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/http2.py
+++ /dev/null
@@ -1,446 +0,0 @@
-import enum
-import time
-from ssl import SSLContext
-from typing import AsyncIterator, Dict, List, Optional, Tuple, cast
-
-import h2.connection
-import h2.events
-from h2.config import H2Configuration
-from h2.exceptions import NoAvailableStreamIDError
-from h2.settings import SettingCodes, Settings
-
-from .._backends.auto import AsyncBackend, AsyncLock, AsyncSemaphore, AsyncSocketStream
-from .._bytestreams import AsyncIteratorByteStream
-from .._exceptions import LocalProtocolError, PoolTimeout, RemoteProtocolError
-from .._types import URL, Headers, TimeoutDict
-from .._utils import get_logger
-from .base import AsyncByteStream, NewConnectionRequired
-from .http import AsyncBaseHTTPConnection
-
-logger = get_logger(__name__)
-
-
-class ConnectionState(enum.IntEnum):
- IDLE = 0
- ACTIVE = 1
- CLOSED = 2
-
-
-class AsyncHTTP2Connection(AsyncBaseHTTPConnection):
- READ_NUM_BYTES = 64 * 1024
- CONFIG = H2Configuration(validate_inbound_headers=False)
-
- def __init__(
- self,
- socket: AsyncSocketStream,
- backend: AsyncBackend,
- keepalive_expiry: float = None,
- ):
- self.socket = socket
-
- self._backend = backend
- self._h2_state = h2.connection.H2Connection(config=self.CONFIG)
-
- self._sent_connection_init = False
- self._streams: Dict[int, AsyncHTTP2Stream] = {}
- self._events: Dict[int, List[h2.events.Event]] = {}
-
- self._keepalive_expiry: Optional[float] = keepalive_expiry
- self._should_expire_at: Optional[float] = None
- self._state = ConnectionState.ACTIVE
- self._exhausted_available_stream_ids = False
-
- def __repr__(self) -> str:
- return f""
-
- def info(self) -> str:
- return f"HTTP/2, {self._state.name}, {len(self._streams)} streams"
-
- def _now(self) -> float:
- return time.monotonic()
-
- def should_close(self) -> bool:
- """
- Return `True` if the connection is currently idle, and the keepalive
- timeout has passed.
- """
- return (
- self._state == ConnectionState.IDLE
- and self._should_expire_at is not None
- and self._now() >= self._should_expire_at
- )
-
- def is_idle(self) -> bool:
- """
- Return `True` if the connection is currently idle.
- """
- return self._state == ConnectionState.IDLE
-
- def is_closed(self) -> bool:
- """
- Return `True` if the connection has been closed.
- """
- return self._state == ConnectionState.CLOSED
-
- def is_available(self) -> bool:
- """
- Return `True` if the connection is currently able to accept an outgoing request.
- This occurs when any of the following occur:
-
- * The connection has not yet been opened, and HTTP/2 support is enabled.
- We don't *know* at this point if we'll end up on an HTTP/2 connection or
- not, but we *might* do, so we indicate availability.
- * The connection has been opened, and is currently idle.
- * The connection is open, and is an HTTP/2 connection. The connection must
- also not have exhausted the maximum total number of stream IDs.
- """
- return (
- self._state != ConnectionState.CLOSED
- and not self._exhausted_available_stream_ids
- )
-
- @property
- def init_lock(self) -> AsyncLock:
- # We do this lazily, to make sure backend autodetection always
- # runs within an async context.
- if not hasattr(self, "_initialization_lock"):
- self._initialization_lock = self._backend.create_lock()
- return self._initialization_lock
-
- @property
- def read_lock(self) -> AsyncLock:
- # We do this lazily, to make sure backend autodetection always
- # runs within an async context.
- if not hasattr(self, "_read_lock"):
- self._read_lock = self._backend.create_lock()
- return self._read_lock
-
- @property
- def max_streams_semaphore(self) -> AsyncSemaphore:
- # We do this lazily, to make sure backend autodetection always
- # runs within an async context.
- if not hasattr(self, "_max_streams_semaphore"):
- max_streams = self._h2_state.local_settings.max_concurrent_streams
- self._max_streams_semaphore = self._backend.create_semaphore(
- max_streams, exc_class=PoolTimeout
- )
- return self._max_streams_semaphore
-
- async def start_tls(
- self, hostname: bytes, ssl_context: SSLContext, timeout: TimeoutDict = None
- ) -> AsyncSocketStream:
- raise NotImplementedError("TLS upgrade not supported on HTTP/2 connections.")
-
- async def handle_async_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: AsyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, AsyncByteStream, dict]:
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
-
- async with self.init_lock:
- if not self._sent_connection_init:
- # The very first stream is responsible for initiating the connection.
- self._state = ConnectionState.ACTIVE
- await self.send_connection_init(timeout)
- self._sent_connection_init = True
-
- await self.max_streams_semaphore.acquire()
- try:
- try:
- stream_id = self._h2_state.get_next_available_stream_id()
- except NoAvailableStreamIDError:
- self._exhausted_available_stream_ids = True
- raise NewConnectionRequired()
- else:
- self._state = ConnectionState.ACTIVE
- self._should_expire_at = None
-
- h2_stream = AsyncHTTP2Stream(stream_id=stream_id, connection=self)
- self._streams[stream_id] = h2_stream
- self._events[stream_id] = []
- return await h2_stream.handle_async_request(
- method, url, headers, stream, extensions
- )
- except Exception: # noqa: PIE786
- await self.max_streams_semaphore.release()
- raise
-
- async def send_connection_init(self, timeout: TimeoutDict) -> None:
- """
- The HTTP/2 connection requires some initial setup before we can start
- using individual request/response streams on it.
- """
- # Need to set these manually here instead of manipulating via
- # __setitem__() otherwise the H2Connection will emit SettingsUpdate
- # frames in addition to sending the undesired defaults.
- self._h2_state.local_settings = Settings(
- client=True,
- initial_values={
- # Disable PUSH_PROMISE frames from the server since we don't do anything
- # with them for now. Maybe when we support caching?
- SettingCodes.ENABLE_PUSH: 0,
- # These two are taken from h2 for safe defaults
- SettingCodes.MAX_CONCURRENT_STREAMS: 100,
- SettingCodes.MAX_HEADER_LIST_SIZE: 65536,
- },
- )
-
- # Some websites (*cough* Yahoo *cough*) balk at this setting being
- # present in the initial handshake since it's not defined in the original
- # RFC despite the RFC mandating ignoring settings you don't know about.
- del self._h2_state.local_settings[
- h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL
- ]
-
- logger.trace("initiate_connection=%r", self)
- self._h2_state.initiate_connection()
- self._h2_state.increment_flow_control_window(2 ** 24)
- data_to_send = self._h2_state.data_to_send()
- await self.socket.write(data_to_send, timeout)
-
- def is_socket_readable(self) -> bool:
- return self.socket.is_readable()
-
- async def aclose(self) -> None:
- logger.trace("close_connection=%r", self)
- if self._state != ConnectionState.CLOSED:
- self._state = ConnectionState.CLOSED
-
- await self.socket.aclose()
-
- async def wait_for_outgoing_flow(self, stream_id: int, timeout: TimeoutDict) -> int:
- """
- Returns the maximum allowable outgoing flow for a given stream.
- If the allowable flow is zero, then waits on the network until
- WindowUpdated frames have increased the flow rate.
- https://tools.ietf.org/html/rfc7540#section-6.9
- """
- local_flow = self._h2_state.local_flow_control_window(stream_id)
- connection_flow = self._h2_state.max_outbound_frame_size
- flow = min(local_flow, connection_flow)
- while flow == 0:
- await self.receive_events(timeout)
- local_flow = self._h2_state.local_flow_control_window(stream_id)
- connection_flow = self._h2_state.max_outbound_frame_size
- flow = min(local_flow, connection_flow)
- return flow
-
- async def wait_for_event(
- self, stream_id: int, timeout: TimeoutDict
- ) -> h2.events.Event:
- """
- Returns the next event for a given stream.
- If no events are available yet, then waits on the network until
- an event is available.
- """
- async with self.read_lock:
- while not self._events[stream_id]:
- await self.receive_events(timeout)
- return self._events[stream_id].pop(0)
-
- async def receive_events(self, timeout: TimeoutDict) -> None:
- """
- Read some data from the network, and update the H2 state.
- """
- data = await self.socket.read(self.READ_NUM_BYTES, timeout)
- if data == b"":
- raise RemoteProtocolError("Server disconnected")
-
- events = self._h2_state.receive_data(data)
- for event in events:
- event_stream_id = getattr(event, "stream_id", 0)
- logger.trace("receive_event stream_id=%r event=%s", event_stream_id, event)
-
- if hasattr(event, "error_code"):
- raise RemoteProtocolError(event)
-
- if event_stream_id in self._events:
- self._events[event_stream_id].append(event)
-
- data_to_send = self._h2_state.data_to_send()
- await self.socket.write(data_to_send, timeout)
-
- async def send_headers(
- self, stream_id: int, headers: Headers, end_stream: bool, timeout: TimeoutDict
- ) -> None:
- logger.trace("send_headers stream_id=%r headers=%r", stream_id, headers)
- self._h2_state.send_headers(stream_id, headers, end_stream=end_stream)
- self._h2_state.increment_flow_control_window(2 ** 24, stream_id=stream_id)
- data_to_send = self._h2_state.data_to_send()
- await self.socket.write(data_to_send, timeout)
-
- async def send_data(
- self, stream_id: int, chunk: bytes, timeout: TimeoutDict
- ) -> None:
- logger.trace("send_data stream_id=%r chunk=%r", stream_id, chunk)
- self._h2_state.send_data(stream_id, chunk)
- data_to_send = self._h2_state.data_to_send()
- await self.socket.write(data_to_send, timeout)
-
- async def end_stream(self, stream_id: int, timeout: TimeoutDict) -> None:
- logger.trace("end_stream stream_id=%r", stream_id)
- self._h2_state.end_stream(stream_id)
- data_to_send = self._h2_state.data_to_send()
- await self.socket.write(data_to_send, timeout)
-
- async def acknowledge_received_data(
- self, stream_id: int, amount: int, timeout: TimeoutDict
- ) -> None:
- self._h2_state.acknowledge_received_data(amount, stream_id)
- data_to_send = self._h2_state.data_to_send()
- await self.socket.write(data_to_send, timeout)
-
- async def close_stream(self, stream_id: int) -> None:
- try:
- logger.trace("close_stream stream_id=%r", stream_id)
- del self._streams[stream_id]
- del self._events[stream_id]
-
- if not self._streams:
- if self._state == ConnectionState.ACTIVE:
- if self._exhausted_available_stream_ids:
- await self.aclose()
- else:
- self._state = ConnectionState.IDLE
- if self._keepalive_expiry is not None:
- self._should_expire_at = (
- self._now() + self._keepalive_expiry
- )
- finally:
- await self.max_streams_semaphore.release()
-
-
-class AsyncHTTP2Stream:
- def __init__(self, stream_id: int, connection: AsyncHTTP2Connection) -> None:
- self.stream_id = stream_id
- self.connection = connection
-
- async def handle_async_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: AsyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, AsyncByteStream, dict]:
- headers = [(k.lower(), v) for (k, v) in headers]
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
-
- # Send the request.
- seen_headers = set(key for key, value in headers)
- has_body = (
- b"content-length" in seen_headers or b"transfer-encoding" in seen_headers
- )
-
- await self.send_headers(method, url, headers, has_body, timeout)
- if has_body:
- await self.send_body(stream, timeout)
-
- # Receive the response.
- status_code, headers = await self.receive_response(timeout)
- response_stream = AsyncIteratorByteStream(
- aiterator=self.body_iter(timeout), aclose_func=self._response_closed
- )
-
- extensions = {
- "http_version": b"HTTP/2",
- }
- return (status_code, headers, response_stream, extensions)
-
- async def send_headers(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- has_body: bool,
- timeout: TimeoutDict,
- ) -> None:
- scheme, hostname, port, path = url
-
- # In HTTP/2 the ':authority' pseudo-header is used instead of 'Host'.
- # In order to gracefully handle HTTP/1.1 and HTTP/2 we always require
- # HTTP/1.1 style headers, and map them appropriately if we end up on
- # an HTTP/2 connection.
- authority = None
-
- for k, v in headers:
- if k == b"host":
- authority = v
- break
-
- if authority is None:
- # Mirror the same error we'd see with `h11`, so that the behaviour
- # is consistent. Although we're dealing with an `:authority`
- # pseudo-header by this point, from an end-user perspective the issue
- # is that the outgoing request needed to include a `host` header.
- raise LocalProtocolError("Missing mandatory Host: header")
-
- headers = [
- (b":method", method),
- (b":authority", authority),
- (b":scheme", scheme),
- (b":path", path),
- ] + [
- (k, v)
- for k, v in headers
- if k
- not in (
- b"host",
- b"transfer-encoding",
- )
- ]
- end_stream = not has_body
-
- await self.connection.send_headers(self.stream_id, headers, end_stream, timeout)
-
- async def send_body(self, stream: AsyncByteStream, timeout: TimeoutDict) -> None:
- async for data in stream:
- while data:
- max_flow = await self.connection.wait_for_outgoing_flow(
- self.stream_id, timeout
- )
- chunk_size = min(len(data), max_flow)
- chunk, data = data[:chunk_size], data[chunk_size:]
- await self.connection.send_data(self.stream_id, chunk, timeout)
-
- await self.connection.end_stream(self.stream_id, timeout)
-
- async def receive_response(
- self, timeout: TimeoutDict
- ) -> Tuple[int, List[Tuple[bytes, bytes]]]:
- """
- Read the response status and headers from the network.
- """
- while True:
- event = await self.connection.wait_for_event(self.stream_id, timeout)
- if isinstance(event, h2.events.ResponseReceived):
- break
-
- status_code = 200
- headers = []
- for k, v in event.headers:
- if k == b":status":
- status_code = int(v.decode("ascii", errors="ignore"))
- elif not k.startswith(b":"):
- headers.append((k, v))
-
- return (status_code, headers)
-
- async def body_iter(self, timeout: TimeoutDict) -> AsyncIterator[bytes]:
- while True:
- event = await self.connection.wait_for_event(self.stream_id, timeout)
- if isinstance(event, h2.events.DataReceived):
- amount = event.flow_controlled_length
- await self.connection.acknowledge_received_data(
- self.stream_id, amount, timeout
- )
- yield event.data
- elif isinstance(event, (h2.events.StreamEnded, h2.events.StreamReset)):
- break
-
- async def _response_closed(self) -> None:
- await self.connection.close_stream(self.stream_id)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/http_proxy.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/http_proxy.py
deleted file mode 100644
index 275bf214..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_async/http_proxy.py
+++ /dev/null
@@ -1,290 +0,0 @@
-from http import HTTPStatus
-from ssl import SSLContext
-from typing import Tuple, cast
-
-from .._bytestreams import ByteStream
-from .._exceptions import ProxyError
-from .._types import URL, Headers, TimeoutDict
-from .._utils import get_logger, url_to_origin
-from .base import AsyncByteStream
-from .connection import AsyncHTTPConnection
-from .connection_pool import AsyncConnectionPool, ResponseByteStream
-
-logger = get_logger(__name__)
-
-
-def get_reason_phrase(status_code: int) -> str:
- try:
- return HTTPStatus(status_code).phrase
- except ValueError:
- return ""
-
-
-def merge_headers(
- default_headers: Headers = None, override_headers: Headers = None
-) -> Headers:
- """
- Append default_headers and override_headers, de-duplicating if a key existing in
- both cases.
- """
- default_headers = [] if default_headers is None else default_headers
- override_headers = [] if override_headers is None else override_headers
- has_override = set([key.lower() for key, value in override_headers])
- default_headers = [
- (key, value)
- for key, value in default_headers
- if key.lower() not in has_override
- ]
- return default_headers + override_headers
-
-
-class AsyncHTTPProxy(AsyncConnectionPool):
- """
- A connection pool for making HTTP requests via an HTTP proxy.
-
- Parameters
- ----------
- proxy_url:
- The URL of the proxy service as a 4-tuple of (scheme, host, port, path).
- proxy_headers:
- A list of proxy headers to include.
- proxy_mode:
- A proxy mode to operate in. May be "DEFAULT", "FORWARD_ONLY", or "TUNNEL_ONLY".
- ssl_context:
- An SSL context to use for verifying connections.
- max_connections:
- The maximum number of concurrent connections to allow.
- max_keepalive_connections:
- The maximum number of connections to allow before closing keep-alive
- connections.
- http2:
- Enable HTTP/2 support.
- """
-
- def __init__(
- self,
- proxy_url: URL,
- proxy_headers: Headers = None,
- proxy_mode: str = "DEFAULT",
- ssl_context: SSLContext = None,
- max_connections: int = None,
- max_keepalive_connections: int = None,
- keepalive_expiry: float = None,
- http2: bool = False,
- backend: str = "auto",
- # Deprecated argument style:
- max_keepalive: int = None,
- ):
- assert proxy_mode in ("DEFAULT", "FORWARD_ONLY", "TUNNEL_ONLY")
-
- self.proxy_origin = url_to_origin(proxy_url)
- self.proxy_headers = [] if proxy_headers is None else proxy_headers
- self.proxy_mode = proxy_mode
- super().__init__(
- ssl_context=ssl_context,
- max_connections=max_connections,
- max_keepalive_connections=max_keepalive_connections,
- keepalive_expiry=keepalive_expiry,
- http2=http2,
- backend=backend,
- max_keepalive=max_keepalive,
- )
-
- async def handle_async_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: AsyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, AsyncByteStream, dict]:
- if self._keepalive_expiry is not None:
- await self._keepalive_sweep()
-
- if (
- self.proxy_mode == "DEFAULT" and url[0] == b"http"
- ) or self.proxy_mode == "FORWARD_ONLY":
- # By default HTTP requests should be forwarded.
- logger.trace(
- "forward_request proxy_origin=%r proxy_headers=%r method=%r url=%r",
- self.proxy_origin,
- self.proxy_headers,
- method,
- url,
- )
- return await self._forward_request(
- method, url, headers=headers, stream=stream, extensions=extensions
- )
- else:
- # By default HTTPS should be tunnelled.
- logger.trace(
- "tunnel_request proxy_origin=%r proxy_headers=%r method=%r url=%r",
- self.proxy_origin,
- self.proxy_headers,
- method,
- url,
- )
- return await self._tunnel_request(
- method, url, headers=headers, stream=stream, extensions=extensions
- )
-
- async def _forward_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: AsyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, AsyncByteStream, dict]:
- """
- Forwarded proxy requests include the entire URL as the HTTP target,
- rather than just the path.
- """
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
- origin = self.proxy_origin
- connection = await self._get_connection_from_pool(origin)
-
- if connection is None:
- connection = AsyncHTTPConnection(
- origin=origin,
- http2=self._http2,
- keepalive_expiry=self._keepalive_expiry,
- ssl_context=self._ssl_context,
- )
- await self._add_to_pool(connection, timeout)
-
- # Issue a forwarded proxy request...
-
- # GET https://www.example.org/path HTTP/1.1
- # [proxy headers]
- # [headers]
- scheme, host, port, path = url
- if port is None:
- target = b"%b://%b%b" % (scheme, host, path)
- else:
- target = b"%b://%b:%d%b" % (scheme, host, port, path)
-
- url = self.proxy_origin + (target,)
- headers = merge_headers(self.proxy_headers, headers)
-
- (
- status_code,
- headers,
- stream,
- extensions,
- ) = await connection.handle_async_request(
- method, url, headers=headers, stream=stream, extensions=extensions
- )
-
- wrapped_stream = ResponseByteStream(
- stream, connection=connection, callback=self._response_closed
- )
-
- return status_code, headers, wrapped_stream, extensions
-
- async def _tunnel_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: AsyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, AsyncByteStream, dict]:
- """
- Tunnelled proxy requests require an initial CONNECT request to
- establish the connection, and then send regular requests.
- """
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
- origin = url_to_origin(url)
- connection = await self._get_connection_from_pool(origin)
-
- if connection is None:
- scheme, host, port = origin
-
- # First, create a connection to the proxy server
- proxy_connection = AsyncHTTPConnection(
- origin=self.proxy_origin,
- http2=self._http2,
- keepalive_expiry=self._keepalive_expiry,
- ssl_context=self._ssl_context,
- )
-
- # Issue a CONNECT request...
-
- # CONNECT www.example.org:80 HTTP/1.1
- # [proxy-headers]
- target = b"%b:%d" % (host, port)
- connect_url = self.proxy_origin + (target,)
- connect_headers = [(b"Host", target), (b"Accept", b"*/*")]
- connect_headers = merge_headers(connect_headers, self.proxy_headers)
-
- try:
- (
- proxy_status_code,
- _,
- proxy_stream,
- _,
- ) = await proxy_connection.handle_async_request(
- b"CONNECT",
- connect_url,
- headers=connect_headers,
- stream=ByteStream(b""),
- extensions=extensions,
- )
-
- proxy_reason = get_reason_phrase(proxy_status_code)
- logger.trace(
- "tunnel_response proxy_status_code=%r proxy_reason=%r ",
- proxy_status_code,
- proxy_reason,
- )
- # Read the response data without closing the socket
- async for _ in proxy_stream:
- pass
-
- # See if the tunnel was successfully established.
- if proxy_status_code < 200 or proxy_status_code > 299:
- msg = "%d %s" % (proxy_status_code, proxy_reason)
- raise ProxyError(msg)
-
- # Upgrade to TLS if required
- # We assume the target speaks TLS on the specified port
- if scheme == b"https":
- await proxy_connection.start_tls(host, self._ssl_context, timeout)
- except Exception as exc:
- await proxy_connection.aclose()
- raise ProxyError(exc)
-
- # The CONNECT request is successful, so we have now SWITCHED PROTOCOLS.
- # This means the proxy connection is now unusable, and we must create
- # a new one for regular requests, making sure to use the same socket to
- # retain the tunnel.
- connection = AsyncHTTPConnection(
- origin=origin,
- http2=self._http2,
- keepalive_expiry=self._keepalive_expiry,
- ssl_context=self._ssl_context,
- socket=proxy_connection.socket,
- )
- await self._add_to_pool(connection, timeout)
-
- # Once the connection has been established we can send requests on
- # it as normal.
- (
- status_code,
- headers,
- stream,
- extensions,
- ) = await connection.handle_async_request(
- method,
- url,
- headers=headers,
- stream=stream,
- extensions=extensions,
- )
-
- wrapped_stream = ResponseByteStream(
- stream, connection=connection, callback=self._response_closed
- )
-
- return status_code, headers, wrapped_stream, extensions
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__init__.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/__init__.cpython-39.pyc
deleted file mode 100644
index 97c5b4ae..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/__init__.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/anyio.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/anyio.cpython-39.pyc
deleted file mode 100644
index 7e5b5572..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/anyio.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/asyncio.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/asyncio.cpython-39.pyc
deleted file mode 100644
index 678c8144..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/asyncio.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/auto.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/auto.cpython-39.pyc
deleted file mode 100644
index ebbfd07f..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/auto.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/base.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/base.cpython-39.pyc
deleted file mode 100644
index 35cdaae4..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/base.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/curio.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/curio.cpython-39.pyc
deleted file mode 100644
index c04a4726..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/curio.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/sync.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/sync.cpython-39.pyc
deleted file mode 100644
index e5b75341..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/sync.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/trio.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/trio.cpython-39.pyc
deleted file mode 100644
index 053e78f2..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/__pycache__/trio.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/anyio.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/anyio.py
deleted file mode 100644
index b1332a27..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/anyio.py
+++ /dev/null
@@ -1,201 +0,0 @@
-from ssl import SSLContext
-from typing import Optional
-
-import anyio.abc
-from anyio import BrokenResourceError, EndOfStream
-from anyio.abc import ByteStream, SocketAttribute
-from anyio.streams.tls import TLSAttribute, TLSStream
-
-from .._exceptions import (
- ConnectError,
- ConnectTimeout,
- ReadError,
- ReadTimeout,
- WriteError,
- WriteTimeout,
- map_exceptions,
-)
-from .._types import TimeoutDict
-from .._utils import is_socket_readable
-from .base import AsyncBackend, AsyncLock, AsyncSemaphore, AsyncSocketStream
-
-
-class SocketStream(AsyncSocketStream):
- def __init__(self, stream: ByteStream) -> None:
- self.stream = stream
- self.read_lock = anyio.Lock()
- self.write_lock = anyio.Lock()
-
- def get_http_version(self) -> str:
- alpn_protocol = self.stream.extra(TLSAttribute.alpn_protocol, None)
- return "HTTP/2" if alpn_protocol == "h2" else "HTTP/1.1"
-
- async def start_tls(
- self,
- hostname: bytes,
- ssl_context: SSLContext,
- timeout: TimeoutDict,
- ) -> "SocketStream":
- connect_timeout = timeout.get("connect")
- try:
- with anyio.fail_after(connect_timeout):
- ssl_stream = await TLSStream.wrap(
- self.stream,
- ssl_context=ssl_context,
- hostname=hostname.decode("ascii"),
- standard_compatible=False,
- )
- except TimeoutError:
- raise ConnectTimeout from None
- except BrokenResourceError as exc:
- raise ConnectError from exc
-
- return SocketStream(ssl_stream)
-
- async def read(self, n: int, timeout: TimeoutDict) -> bytes:
- read_timeout = timeout.get("read")
- async with self.read_lock:
- try:
- with anyio.fail_after(read_timeout):
- return await self.stream.receive(n)
- except TimeoutError:
- await self.stream.aclose()
- raise ReadTimeout from None
- except BrokenResourceError as exc:
- raise ReadError from exc
- except EndOfStream:
- return b""
-
- async def write(self, data: bytes, timeout: TimeoutDict) -> None:
- if not data:
- return
-
- write_timeout = timeout.get("write")
- async with self.write_lock:
- try:
- with anyio.fail_after(write_timeout):
- return await self.stream.send(data)
- except TimeoutError:
- await self.stream.aclose()
- raise WriteTimeout from None
- except BrokenResourceError as exc:
- raise WriteError from exc
-
- async def aclose(self) -> None:
- async with self.write_lock:
- try:
- await self.stream.aclose()
- except BrokenResourceError:
- pass
-
- def is_readable(self) -> bool:
- sock = self.stream.extra(SocketAttribute.raw_socket)
- return is_socket_readable(sock)
-
-
-class Lock(AsyncLock):
- def __init__(self) -> None:
- self._lock = anyio.Lock()
-
- async def release(self) -> None:
- self._lock.release()
-
- async def acquire(self) -> None:
- await self._lock.acquire()
-
-
-class Semaphore(AsyncSemaphore):
- def __init__(self, max_value: int, exc_class: type):
- self.max_value = max_value
- self.exc_class = exc_class
-
- @property
- def semaphore(self) -> anyio.abc.Semaphore:
- if not hasattr(self, "_semaphore"):
- self._semaphore = anyio.Semaphore(self.max_value)
- return self._semaphore
-
- async def acquire(self, timeout: float = None) -> None:
- with anyio.move_on_after(timeout):
- await self.semaphore.acquire()
- return
-
- raise self.exc_class()
-
- async def release(self) -> None:
- self.semaphore.release()
-
-
-class AnyIOBackend(AsyncBackend):
- async def open_tcp_stream(
- self,
- hostname: bytes,
- port: int,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- *,
- local_address: Optional[str],
- ) -> AsyncSocketStream:
- connect_timeout = timeout.get("connect")
- unicode_host = hostname.decode("utf-8")
- exc_map = {
- TimeoutError: ConnectTimeout,
- OSError: ConnectError,
- BrokenResourceError: ConnectError,
- }
-
- with map_exceptions(exc_map):
- with anyio.fail_after(connect_timeout):
- stream: anyio.abc.ByteStream
- stream = await anyio.connect_tcp(
- unicode_host, port, local_host=local_address
- )
- if ssl_context:
- stream = await TLSStream.wrap(
- stream,
- hostname=unicode_host,
- ssl_context=ssl_context,
- standard_compatible=False,
- )
-
- return SocketStream(stream=stream)
-
- async def open_uds_stream(
- self,
- path: str,
- hostname: bytes,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- ) -> AsyncSocketStream:
- connect_timeout = timeout.get("connect")
- unicode_host = hostname.decode("utf-8")
- exc_map = {
- TimeoutError: ConnectTimeout,
- OSError: ConnectError,
- BrokenResourceError: ConnectError,
- }
-
- with map_exceptions(exc_map):
- with anyio.fail_after(connect_timeout):
- stream: anyio.abc.ByteStream = await anyio.connect_unix(path)
- if ssl_context:
- stream = await TLSStream.wrap(
- stream,
- hostname=unicode_host,
- ssl_context=ssl_context,
- standard_compatible=False,
- )
-
- return SocketStream(stream=stream)
-
- def create_lock(self) -> AsyncLock:
- return Lock()
-
- def create_semaphore(self, max_value: int, exc_class: type) -> AsyncSemaphore:
- return Semaphore(max_value, exc_class=exc_class)
-
- async def time(self) -> float:
- return float(anyio.current_time())
-
- async def sleep(self, seconds: float) -> None:
- await anyio.sleep(seconds)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/asyncio.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/asyncio.py
deleted file mode 100644
index 5142072e..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/asyncio.py
+++ /dev/null
@@ -1,303 +0,0 @@
-import asyncio
-import socket
-from ssl import SSLContext
-from typing import Optional
-
-from .._exceptions import (
- ConnectError,
- ConnectTimeout,
- ReadError,
- ReadTimeout,
- WriteError,
- WriteTimeout,
- map_exceptions,
-)
-from .._types import TimeoutDict
-from .._utils import is_socket_readable
-from .base import AsyncBackend, AsyncLock, AsyncSemaphore, AsyncSocketStream
-
-SSL_MONKEY_PATCH_APPLIED = False
-
-
-def ssl_monkey_patch() -> None:
- """
- Monkey-patch for https://bugs.python.org/issue36709
-
- This prevents console errors when outstanding HTTPS connections
- still exist at the point of exiting.
-
- Clients which have been opened using a `with` block, or which have
- had `close()` closed, will not exhibit this issue in the first place.
- """
- MonkeyPatch = asyncio.selector_events._SelectorSocketTransport # type: ignore
-
- _write = MonkeyPatch.write
-
- def _fixed_write(self, data: bytes) -> None: # type: ignore
- if self._loop and not self._loop.is_closed():
- _write(self, data)
-
- MonkeyPatch.write = _fixed_write
-
-
-async def backport_start_tls(
- transport: asyncio.BaseTransport,
- protocol: asyncio.BaseProtocol,
- ssl_context: SSLContext,
- *,
- server_side: bool = False,
- server_hostname: str = None,
- ssl_handshake_timeout: float = None,
-) -> asyncio.Transport: # pragma: nocover (Since it's not used on all Python versions.)
- """
- Python 3.6 asyncio doesn't have a start_tls() method on the loop
- so we use this function in place of the loop's start_tls() method.
- Adapted from this comment:
- https://github.com/urllib3/urllib3/issues/1323#issuecomment-362494839
- """
- import asyncio.sslproto
-
- loop = asyncio.get_event_loop()
- waiter = loop.create_future()
- ssl_protocol = asyncio.sslproto.SSLProtocol(
- loop,
- protocol,
- ssl_context,
- waiter,
- server_side=False,
- server_hostname=server_hostname,
- call_connection_made=False,
- )
-
- transport.set_protocol(ssl_protocol)
- loop.call_soon(ssl_protocol.connection_made, transport)
- loop.call_soon(transport.resume_reading) # type: ignore
-
- await waiter
- return ssl_protocol._app_transport
-
-
-class SocketStream(AsyncSocketStream):
- def __init__(
- self, stream_reader: asyncio.StreamReader, stream_writer: asyncio.StreamWriter
- ):
- self.stream_reader = stream_reader
- self.stream_writer = stream_writer
- self.read_lock = asyncio.Lock()
- self.write_lock = asyncio.Lock()
-
- def get_http_version(self) -> str:
- ssl_object = self.stream_writer.get_extra_info("ssl_object")
-
- if ssl_object is None:
- return "HTTP/1.1"
-
- ident = ssl_object.selected_alpn_protocol()
- return "HTTP/2" if ident == "h2" else "HTTP/1.1"
-
- async def start_tls(
- self, hostname: bytes, ssl_context: SSLContext, timeout: TimeoutDict
- ) -> "SocketStream":
- loop = asyncio.get_event_loop()
-
- stream_reader = asyncio.StreamReader()
- protocol = asyncio.StreamReaderProtocol(stream_reader)
- transport = self.stream_writer.transport
-
- loop_start_tls = getattr(loop, "start_tls", backport_start_tls)
-
- exc_map = {asyncio.TimeoutError: ConnectTimeout, OSError: ConnectError}
-
- with map_exceptions(exc_map):
- transport = await asyncio.wait_for(
- loop_start_tls(
- transport,
- protocol,
- ssl_context,
- server_hostname=hostname.decode("ascii"),
- ),
- timeout=timeout.get("connect"),
- )
-
- # Initialize the protocol, so it is made aware of being tied to
- # a TLS connection.
- # See: https://github.com/encode/httpx/issues/859
- protocol.connection_made(transport)
-
- stream_writer = asyncio.StreamWriter(
- transport=transport, protocol=protocol, reader=stream_reader, loop=loop
- )
-
- ssl_stream = SocketStream(stream_reader, stream_writer)
- # When we return a new SocketStream with new StreamReader/StreamWriter instances
- # we need to keep references to the old StreamReader/StreamWriter so that they
- # are not garbage collected and closed while we're still using them.
- ssl_stream._inner = self # type: ignore
- return ssl_stream
-
- async def read(self, n: int, timeout: TimeoutDict) -> bytes:
- exc_map = {asyncio.TimeoutError: ReadTimeout, OSError: ReadError}
- async with self.read_lock:
- with map_exceptions(exc_map):
- try:
- return await asyncio.wait_for(
- self.stream_reader.read(n), timeout.get("read")
- )
- except AttributeError as exc: # pragma: nocover
- if "resume_reading" in str(exc):
- # Python's asyncio has a bug that can occur when a
- # connection has been closed, while it is paused.
- # See: https://github.com/encode/httpx/issues/1213
- #
- # Returning an empty byte-string to indicate connection
- # close will eventually raise an httpcore.RemoteProtocolError
- # to the user when this goes through our HTTP parsing layer.
- return b""
- raise
-
- async def write(self, data: bytes, timeout: TimeoutDict) -> None:
- if not data:
- return
-
- exc_map = {asyncio.TimeoutError: WriteTimeout, OSError: WriteError}
- async with self.write_lock:
- with map_exceptions(exc_map):
- self.stream_writer.write(data)
- return await asyncio.wait_for(
- self.stream_writer.drain(), timeout.get("write")
- )
-
- async def aclose(self) -> None:
- # SSL connections should issue the close and then abort, rather than
- # waiting for the remote end of the connection to signal the EOF.
- #
- # See:
- #
- # * https://bugs.python.org/issue39758
- # * https://github.com/python-trio/trio/blob/
- # 31e2ae866ad549f1927d45ce073d4f0ea9f12419/trio/_ssl.py#L779-L829
- #
- # And related issues caused if we simply omit the 'wait_closed' call,
- # without first using `.abort()`
- #
- # * https://github.com/encode/httpx/issues/825
- # * https://github.com/encode/httpx/issues/914
- is_ssl = self.stream_writer.get_extra_info("ssl_object") is not None
-
- async with self.write_lock:
- try:
- self.stream_writer.close()
- if is_ssl:
- # Give the connection a chance to write any data in the buffer,
- # and then forcibly tear down the SSL connection.
- await asyncio.sleep(0)
- self.stream_writer.transport.abort() # type: ignore
- if hasattr(self.stream_writer, "wait_closed"):
- # Python 3.7+ only.
- await self.stream_writer.wait_closed() # type: ignore
- except OSError:
- pass
-
- def is_readable(self) -> bool:
- transport = self.stream_reader._transport # type: ignore
- sock: Optional[socket.socket] = transport.get_extra_info("socket")
- return is_socket_readable(sock)
-
-
-class Lock(AsyncLock):
- def __init__(self) -> None:
- self._lock = asyncio.Lock()
-
- async def release(self) -> None:
- self._lock.release()
-
- async def acquire(self) -> None:
- await self._lock.acquire()
-
-
-class Semaphore(AsyncSemaphore):
- def __init__(self, max_value: int, exc_class: type) -> None:
- self.max_value = max_value
- self.exc_class = exc_class
-
- @property
- def semaphore(self) -> asyncio.BoundedSemaphore:
- if not hasattr(self, "_semaphore"):
- self._semaphore = asyncio.BoundedSemaphore(value=self.max_value)
- return self._semaphore
-
- async def acquire(self, timeout: float = None) -> None:
- try:
- await asyncio.wait_for(self.semaphore.acquire(), timeout)
- except asyncio.TimeoutError:
- raise self.exc_class()
-
- async def release(self) -> None:
- self.semaphore.release()
-
-
-class AsyncioBackend(AsyncBackend):
- def __init__(self) -> None:
- global SSL_MONKEY_PATCH_APPLIED
-
- if not SSL_MONKEY_PATCH_APPLIED:
- ssl_monkey_patch()
- SSL_MONKEY_PATCH_APPLIED = True
-
- async def open_tcp_stream(
- self,
- hostname: bytes,
- port: int,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- *,
- local_address: Optional[str],
- ) -> SocketStream:
- host = hostname.decode("ascii")
- connect_timeout = timeout.get("connect")
- local_addr = None if local_address is None else (local_address, 0)
-
- exc_map = {asyncio.TimeoutError: ConnectTimeout, OSError: ConnectError}
- with map_exceptions(exc_map):
- stream_reader, stream_writer = await asyncio.wait_for(
- asyncio.open_connection(
- host, port, ssl=ssl_context, local_addr=local_addr
- ),
- connect_timeout,
- )
- return SocketStream(
- stream_reader=stream_reader, stream_writer=stream_writer
- )
-
- async def open_uds_stream(
- self,
- path: str,
- hostname: bytes,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- ) -> AsyncSocketStream:
- host = hostname.decode("ascii")
- connect_timeout = timeout.get("connect")
- kwargs: dict = {"server_hostname": host} if ssl_context is not None else {}
- exc_map = {asyncio.TimeoutError: ConnectTimeout, OSError: ConnectError}
- with map_exceptions(exc_map):
- stream_reader, stream_writer = await asyncio.wait_for(
- asyncio.open_unix_connection(path, ssl=ssl_context, **kwargs),
- connect_timeout,
- )
- return SocketStream(
- stream_reader=stream_reader, stream_writer=stream_writer
- )
-
- def create_lock(self) -> AsyncLock:
- return Lock()
-
- def create_semaphore(self, max_value: int, exc_class: type) -> AsyncSemaphore:
- return Semaphore(max_value, exc_class=exc_class)
-
- async def time(self) -> float:
- loop = asyncio.get_event_loop()
- return loop.time()
-
- async def sleep(self, seconds: float) -> None:
- await asyncio.sleep(seconds)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/auto.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/auto.py
deleted file mode 100644
index 5579ab46..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/auto.py
+++ /dev/null
@@ -1,67 +0,0 @@
-from ssl import SSLContext
-from typing import Optional
-
-import sniffio
-
-from .._types import TimeoutDict
-from .base import AsyncBackend, AsyncLock, AsyncSemaphore, AsyncSocketStream
-
-# The following line is imported from the _sync modules
-from .sync import SyncBackend, SyncLock, SyncSemaphore, SyncSocketStream # noqa
-
-
-class AutoBackend(AsyncBackend):
- @property
- def backend(self) -> AsyncBackend:
- if not hasattr(self, "_backend_implementation"):
- backend = sniffio.current_async_library()
-
- if backend == "asyncio":
- from .anyio import AnyIOBackend
-
- self._backend_implementation: AsyncBackend = AnyIOBackend()
- elif backend == "trio":
- from .trio import TrioBackend
-
- self._backend_implementation = TrioBackend()
- elif backend == "curio":
- from .curio import CurioBackend
-
- self._backend_implementation = CurioBackend()
- else: # pragma: nocover
- raise RuntimeError(f"Unsupported concurrency backend {backend!r}")
- return self._backend_implementation
-
- async def open_tcp_stream(
- self,
- hostname: bytes,
- port: int,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- *,
- local_address: Optional[str],
- ) -> AsyncSocketStream:
- return await self.backend.open_tcp_stream(
- hostname, port, ssl_context, timeout, local_address=local_address
- )
-
- async def open_uds_stream(
- self,
- path: str,
- hostname: bytes,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- ) -> AsyncSocketStream:
- return await self.backend.open_uds_stream(path, hostname, ssl_context, timeout)
-
- def create_lock(self) -> AsyncLock:
- return self.backend.create_lock()
-
- def create_semaphore(self, max_value: int, exc_class: type) -> AsyncSemaphore:
- return self.backend.create_semaphore(max_value, exc_class=exc_class)
-
- async def time(self) -> float:
- return await self.backend.time()
-
- async def sleep(self, seconds: float) -> None:
- await self.backend.sleep(seconds)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/base.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/base.py
deleted file mode 100644
index 1ca6e31b..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/base.py
+++ /dev/null
@@ -1,137 +0,0 @@
-from ssl import SSLContext
-from types import TracebackType
-from typing import TYPE_CHECKING, Optional, Type
-
-from .._types import TimeoutDict
-
-if TYPE_CHECKING: # pragma: no cover
- from .sync import SyncBackend
-
-
-def lookup_async_backend(name: str) -> "AsyncBackend":
- if name == "auto":
- from .auto import AutoBackend
-
- return AutoBackend()
- elif name == "asyncio":
- from .asyncio import AsyncioBackend
-
- return AsyncioBackend()
- elif name == "trio":
- from .trio import TrioBackend
-
- return TrioBackend()
- elif name == "curio":
- from .curio import CurioBackend
-
- return CurioBackend()
- elif name == "anyio":
- from .anyio import AnyIOBackend
-
- return AnyIOBackend()
-
- raise ValueError("Invalid backend name {name!r}")
-
-
-def lookup_sync_backend(name: str) -> "SyncBackend":
- from .sync import SyncBackend
-
- return SyncBackend()
-
-
-class AsyncSocketStream:
- """
- A socket stream with read/write operations. Abstracts away any asyncio-specific
- interfaces into a more generic base class, that we can use with alternate
- backends, or for stand-alone test cases.
- """
-
- def get_http_version(self) -> str:
- raise NotImplementedError() # pragma: no cover
-
- async def start_tls(
- self, hostname: bytes, ssl_context: SSLContext, timeout: TimeoutDict
- ) -> "AsyncSocketStream":
- raise NotImplementedError() # pragma: no cover
-
- async def read(self, n: int, timeout: TimeoutDict) -> bytes:
- raise NotImplementedError() # pragma: no cover
-
- async def write(self, data: bytes, timeout: TimeoutDict) -> None:
- raise NotImplementedError() # pragma: no cover
-
- async def aclose(self) -> None:
- raise NotImplementedError() # pragma: no cover
-
- def is_readable(self) -> bool:
- raise NotImplementedError() # pragma: no cover
-
-
-class AsyncLock:
- """
- An abstract interface for Lock classes.
- """
-
- async def __aenter__(self) -> None:
- await self.acquire()
-
- async def __aexit__(
- self,
- exc_type: Type[BaseException] = None,
- exc_value: BaseException = None,
- traceback: TracebackType = None,
- ) -> None:
- await self.release()
-
- async def release(self) -> None:
- raise NotImplementedError() # pragma: no cover
-
- async def acquire(self) -> None:
- raise NotImplementedError() # pragma: no cover
-
-
-class AsyncSemaphore:
- """
- An abstract interface for Semaphore classes.
- Abstracts away any asyncio-specific interfaces.
- """
-
- async def acquire(self, timeout: float = None) -> None:
- raise NotImplementedError() # pragma: no cover
-
- async def release(self) -> None:
- raise NotImplementedError() # pragma: no cover
-
-
-class AsyncBackend:
- async def open_tcp_stream(
- self,
- hostname: bytes,
- port: int,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- *,
- local_address: Optional[str],
- ) -> AsyncSocketStream:
- raise NotImplementedError() # pragma: no cover
-
- async def open_uds_stream(
- self,
- path: str,
- hostname: bytes,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- ) -> AsyncSocketStream:
- raise NotImplementedError() # pragma: no cover
-
- def create_lock(self) -> AsyncLock:
- raise NotImplementedError() # pragma: no cover
-
- def create_semaphore(self, max_value: int, exc_class: type) -> AsyncSemaphore:
- raise NotImplementedError() # pragma: no cover
-
- async def time(self) -> float:
- raise NotImplementedError() # pragma: no cover
-
- async def sleep(self, seconds: float) -> None:
- raise NotImplementedError() # pragma: no cover
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/curio.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/curio.py
deleted file mode 100644
index 99a7b2cc..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/curio.py
+++ /dev/null
@@ -1,206 +0,0 @@
-from ssl import SSLContext, SSLSocket
-from typing import Optional
-
-import curio
-import curio.io
-
-from .._exceptions import (
- ConnectError,
- ConnectTimeout,
- ReadError,
- ReadTimeout,
- WriteError,
- WriteTimeout,
- map_exceptions,
-)
-from .._types import TimeoutDict
-from .._utils import get_logger, is_socket_readable
-from .base import AsyncBackend, AsyncLock, AsyncSemaphore, AsyncSocketStream
-
-logger = get_logger(__name__)
-
-ONE_DAY_IN_SECONDS = float(60 * 60 * 24)
-
-
-def convert_timeout(value: Optional[float]) -> float:
- return value if value is not None else ONE_DAY_IN_SECONDS
-
-
-class Lock(AsyncLock):
- def __init__(self) -> None:
- self._lock = curio.Lock()
-
- async def acquire(self) -> None:
- await self._lock.acquire()
-
- async def release(self) -> None:
- await self._lock.release()
-
-
-class Semaphore(AsyncSemaphore):
- def __init__(self, max_value: int, exc_class: type) -> None:
- self.max_value = max_value
- self.exc_class = exc_class
-
- @property
- def semaphore(self) -> curio.Semaphore:
- if not hasattr(self, "_semaphore"):
- self._semaphore = curio.Semaphore(value=self.max_value)
- return self._semaphore
-
- async def acquire(self, timeout: float = None) -> None:
- timeout = convert_timeout(timeout)
-
- try:
- return await curio.timeout_after(timeout, self.semaphore.acquire())
- except curio.TaskTimeout:
- raise self.exc_class()
-
- async def release(self) -> None:
- await self.semaphore.release()
-
-
-class SocketStream(AsyncSocketStream):
- def __init__(self, socket: curio.io.Socket) -> None:
- self.read_lock = curio.Lock()
- self.write_lock = curio.Lock()
- self.socket = socket
- self.stream = socket.as_stream()
-
- def get_http_version(self) -> str:
- if hasattr(self.socket, "_socket"):
- raw_socket = self.socket._socket
-
- if isinstance(raw_socket, SSLSocket):
- ident = raw_socket.selected_alpn_protocol()
- return "HTTP/2" if ident == "h2" else "HTTP/1.1"
-
- return "HTTP/1.1"
-
- async def start_tls(
- self, hostname: bytes, ssl_context: SSLContext, timeout: TimeoutDict
- ) -> "AsyncSocketStream":
- connect_timeout = convert_timeout(timeout.get("connect"))
- exc_map = {
- curio.TaskTimeout: ConnectTimeout,
- curio.CurioError: ConnectError,
- OSError: ConnectError,
- }
-
- with map_exceptions(exc_map):
- wrapped_sock = curio.io.Socket(
- ssl_context.wrap_socket(
- self.socket._socket,
- do_handshake_on_connect=False,
- server_hostname=hostname.decode("ascii"),
- )
- )
-
- await curio.timeout_after(
- connect_timeout,
- wrapped_sock.do_handshake(),
- )
-
- return SocketStream(wrapped_sock)
-
- async def read(self, n: int, timeout: TimeoutDict) -> bytes:
- read_timeout = convert_timeout(timeout.get("read"))
- exc_map = {
- curio.TaskTimeout: ReadTimeout,
- curio.CurioError: ReadError,
- OSError: ReadError,
- }
-
- with map_exceptions(exc_map):
- async with self.read_lock:
- return await curio.timeout_after(read_timeout, self.stream.read(n))
-
- async def write(self, data: bytes, timeout: TimeoutDict) -> None:
- write_timeout = convert_timeout(timeout.get("write"))
- exc_map = {
- curio.TaskTimeout: WriteTimeout,
- curio.CurioError: WriteError,
- OSError: WriteError,
- }
-
- with map_exceptions(exc_map):
- async with self.write_lock:
- await curio.timeout_after(write_timeout, self.stream.write(data))
-
- async def aclose(self) -> None:
- await self.stream.close()
- await self.socket.close()
-
- def is_readable(self) -> bool:
- return is_socket_readable(self.socket)
-
-
-class CurioBackend(AsyncBackend):
- async def open_tcp_stream(
- self,
- hostname: bytes,
- port: int,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- *,
- local_address: Optional[str],
- ) -> AsyncSocketStream:
- connect_timeout = convert_timeout(timeout.get("connect"))
- exc_map = {
- curio.TaskTimeout: ConnectTimeout,
- curio.CurioError: ConnectError,
- OSError: ConnectError,
- }
- host = hostname.decode("ascii")
-
- kwargs: dict = {}
- if ssl_context is not None:
- kwargs["ssl"] = ssl_context
- kwargs["server_hostname"] = host
- if local_address is not None:
- kwargs["source_addr"] = (local_address, 0)
-
- with map_exceptions(exc_map):
- sock: curio.io.Socket = await curio.timeout_after(
- connect_timeout,
- curio.open_connection(hostname, port, **kwargs),
- )
-
- return SocketStream(sock)
-
- async def open_uds_stream(
- self,
- path: str,
- hostname: bytes,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- ) -> AsyncSocketStream:
- connect_timeout = convert_timeout(timeout.get("connect"))
- exc_map = {
- curio.TaskTimeout: ConnectTimeout,
- curio.CurioError: ConnectError,
- OSError: ConnectError,
- }
- host = hostname.decode("ascii")
- kwargs = (
- {} if ssl_context is None else {"ssl": ssl_context, "server_hostname": host}
- )
-
- with map_exceptions(exc_map):
- sock: curio.io.Socket = await curio.timeout_after(
- connect_timeout, curio.open_unix_connection(path, **kwargs)
- )
-
- return SocketStream(sock)
-
- def create_lock(self) -> AsyncLock:
- return Lock()
-
- def create_semaphore(self, max_value: int, exc_class: type) -> AsyncSemaphore:
- return Semaphore(max_value, exc_class)
-
- async def time(self) -> float:
- return await curio.clock()
-
- async def sleep(self, seconds: float) -> None:
- await curio.sleep(seconds)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/sync.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/sync.py
deleted file mode 100644
index ee8f94b7..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/sync.py
+++ /dev/null
@@ -1,178 +0,0 @@
-import socket
-import threading
-import time
-from ssl import SSLContext
-from types import TracebackType
-from typing import Optional, Type
-
-from .._exceptions import (
- ConnectError,
- ConnectTimeout,
- ReadError,
- ReadTimeout,
- WriteError,
- WriteTimeout,
- map_exceptions,
-)
-from .._types import TimeoutDict
-from .._utils import is_socket_readable
-
-
-class SyncSocketStream:
- """
- A socket stream with read/write operations. Abstracts away any asyncio-specific
- interfaces into a more generic base class, that we can use with alternate
- backends, or for stand-alone test cases.
- """
-
- def __init__(self, sock: socket.socket) -> None:
- self.sock = sock
- self.read_lock = threading.Lock()
- self.write_lock = threading.Lock()
-
- def get_http_version(self) -> str:
- selected_alpn_protocol = getattr(self.sock, "selected_alpn_protocol", None)
- if selected_alpn_protocol is not None:
- ident = selected_alpn_protocol()
- return "HTTP/2" if ident == "h2" else "HTTP/1.1"
- return "HTTP/1.1"
-
- def start_tls(
- self, hostname: bytes, ssl_context: SSLContext, timeout: TimeoutDict
- ) -> "SyncSocketStream":
- connect_timeout = timeout.get("connect")
- exc_map = {socket.timeout: ConnectTimeout, socket.error: ConnectError}
-
- with map_exceptions(exc_map):
- self.sock.settimeout(connect_timeout)
- wrapped = ssl_context.wrap_socket(
- self.sock, server_hostname=hostname.decode("ascii")
- )
-
- return SyncSocketStream(wrapped)
-
- def read(self, n: int, timeout: TimeoutDict) -> bytes:
- read_timeout = timeout.get("read")
- exc_map = {socket.timeout: ReadTimeout, socket.error: ReadError}
-
- with self.read_lock:
- with map_exceptions(exc_map):
- self.sock.settimeout(read_timeout)
- return self.sock.recv(n)
-
- def write(self, data: bytes, timeout: TimeoutDict) -> None:
- write_timeout = timeout.get("write")
- exc_map = {socket.timeout: WriteTimeout, socket.error: WriteError}
-
- with self.write_lock:
- with map_exceptions(exc_map):
- while data:
- self.sock.settimeout(write_timeout)
- n = self.sock.send(data)
- data = data[n:]
-
- def close(self) -> None:
- with self.write_lock:
- try:
- self.sock.close()
- except socket.error:
- pass
-
- def is_readable(self) -> bool:
- return is_socket_readable(self.sock)
-
-
-class SyncLock:
- def __init__(self) -> None:
- self._lock = threading.Lock()
-
- def __enter__(self) -> None:
- self.acquire()
-
- def __exit__(
- self,
- exc_type: Type[BaseException] = None,
- exc_value: BaseException = None,
- traceback: TracebackType = None,
- ) -> None:
- self.release()
-
- def release(self) -> None:
- self._lock.release()
-
- def acquire(self) -> None:
- self._lock.acquire()
-
-
-class SyncSemaphore:
- def __init__(self, max_value: int, exc_class: type) -> None:
- self.max_value = max_value
- self.exc_class = exc_class
- self._semaphore = threading.Semaphore(max_value)
-
- def acquire(self, timeout: float = None) -> None:
- if not self._semaphore.acquire(timeout=timeout): # type: ignore
- raise self.exc_class()
-
- def release(self) -> None:
- self._semaphore.release()
-
-
-class SyncBackend:
- def open_tcp_stream(
- self,
- hostname: bytes,
- port: int,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- *,
- local_address: Optional[str],
- ) -> SyncSocketStream:
- address = (hostname.decode("ascii"), port)
- connect_timeout = timeout.get("connect")
- source_address = None if local_address is None else (local_address, 0)
- exc_map = {socket.timeout: ConnectTimeout, socket.error: ConnectError}
-
- with map_exceptions(exc_map):
- sock = socket.create_connection(
- address, connect_timeout, source_address=source_address # type: ignore
- )
- if ssl_context is not None:
- sock = ssl_context.wrap_socket(
- sock, server_hostname=hostname.decode("ascii")
- )
- return SyncSocketStream(sock=sock)
-
- def open_uds_stream(
- self,
- path: str,
- hostname: bytes,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- ) -> SyncSocketStream:
- connect_timeout = timeout.get("connect")
- exc_map = {socket.timeout: ConnectTimeout, socket.error: ConnectError}
-
- with map_exceptions(exc_map):
- sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- sock.settimeout(connect_timeout)
- sock.connect(path)
-
- if ssl_context is not None:
- sock = ssl_context.wrap_socket(
- sock, server_hostname=hostname.decode("ascii")
- )
-
- return SyncSocketStream(sock=sock)
-
- def create_lock(self) -> SyncLock:
- return SyncLock()
-
- def create_semaphore(self, max_value: int, exc_class: type) -> SyncSemaphore:
- return SyncSemaphore(max_value, exc_class=exc_class)
-
- def time(self) -> float:
- return time.monotonic()
-
- def sleep(self, seconds: float) -> None:
- time.sleep(seconds)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/trio.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/trio.py
deleted file mode 100644
index d6e67c2e..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_backends/trio.py
+++ /dev/null
@@ -1,212 +0,0 @@
-from ssl import SSLContext
-from typing import Optional
-
-import trio
-
-from .._exceptions import (
- ConnectError,
- ConnectTimeout,
- ReadError,
- ReadTimeout,
- WriteError,
- WriteTimeout,
- map_exceptions,
-)
-from .._types import TimeoutDict
-from .base import AsyncBackend, AsyncLock, AsyncSemaphore, AsyncSocketStream
-
-
-def none_as_inf(value: Optional[float]) -> float:
- return value if value is not None else float("inf")
-
-
-class SocketStream(AsyncSocketStream):
- def __init__(self, stream: trio.abc.Stream) -> None:
- self.stream = stream
- self.read_lock = trio.Lock()
- self.write_lock = trio.Lock()
-
- def get_http_version(self) -> str:
- if not isinstance(self.stream, trio.SSLStream):
- return "HTTP/1.1"
-
- ident = self.stream.selected_alpn_protocol()
- return "HTTP/2" if ident == "h2" else "HTTP/1.1"
-
- async def start_tls(
- self, hostname: bytes, ssl_context: SSLContext, timeout: TimeoutDict
- ) -> "SocketStream":
- connect_timeout = none_as_inf(timeout.get("connect"))
- exc_map = {
- trio.TooSlowError: ConnectTimeout,
- trio.BrokenResourceError: ConnectError,
- }
- ssl_stream = trio.SSLStream(
- self.stream,
- ssl_context=ssl_context,
- server_hostname=hostname.decode("ascii"),
- )
-
- with map_exceptions(exc_map):
- with trio.fail_after(connect_timeout):
- await ssl_stream.do_handshake()
- return SocketStream(ssl_stream)
-
- async def read(self, n: int, timeout: TimeoutDict) -> bytes:
- read_timeout = none_as_inf(timeout.get("read"))
- exc_map = {trio.TooSlowError: ReadTimeout, trio.BrokenResourceError: ReadError}
-
- async with self.read_lock:
- with map_exceptions(exc_map):
- try:
- with trio.fail_after(read_timeout):
- return await self.stream.receive_some(max_bytes=n)
- except trio.TooSlowError as exc:
- await self.stream.aclose()
- raise exc
-
- async def write(self, data: bytes, timeout: TimeoutDict) -> None:
- if not data:
- return
-
- write_timeout = none_as_inf(timeout.get("write"))
- exc_map = {
- trio.TooSlowError: WriteTimeout,
- trio.BrokenResourceError: WriteError,
- }
-
- async with self.write_lock:
- with map_exceptions(exc_map):
- try:
- with trio.fail_after(write_timeout):
- return await self.stream.send_all(data)
- except trio.TooSlowError as exc:
- await self.stream.aclose()
- raise exc
-
- async def aclose(self) -> None:
- async with self.write_lock:
- try:
- await self.stream.aclose()
- except trio.BrokenResourceError:
- pass
-
- def is_readable(self) -> bool:
- # Adapted from: https://github.com/encode/httpx/pull/143#issuecomment-515202982
- stream = self.stream
-
- # Peek through any SSLStream wrappers to get the underlying SocketStream.
- while isinstance(stream, trio.SSLStream):
- stream = stream.transport_stream
- assert isinstance(stream, trio.SocketStream)
-
- return stream.socket.is_readable()
-
-
-class Lock(AsyncLock):
- def __init__(self) -> None:
- self._lock = trio.Lock()
-
- async def release(self) -> None:
- self._lock.release()
-
- async def acquire(self) -> None:
- await self._lock.acquire()
-
-
-class Semaphore(AsyncSemaphore):
- def __init__(self, max_value: int, exc_class: type):
- self.max_value = max_value
- self.exc_class = exc_class
-
- @property
- def semaphore(self) -> trio.Semaphore:
- if not hasattr(self, "_semaphore"):
- self._semaphore = trio.Semaphore(self.max_value, max_value=self.max_value)
- return self._semaphore
-
- async def acquire(self, timeout: float = None) -> None:
- timeout = none_as_inf(timeout)
-
- with trio.move_on_after(timeout):
- await self.semaphore.acquire()
- return
-
- raise self.exc_class()
-
- async def release(self) -> None:
- self.semaphore.release()
-
-
-class TrioBackend(AsyncBackend):
- async def open_tcp_stream(
- self,
- hostname: bytes,
- port: int,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- *,
- local_address: Optional[str],
- ) -> AsyncSocketStream:
- connect_timeout = none_as_inf(timeout.get("connect"))
- # Trio will support local_address from 0.16.1 onwards.
- # We only include the keyword argument if a local_address
- # argument has been passed.
- kwargs: dict = {} if local_address is None else {"local_address": local_address}
- exc_map = {
- OSError: ConnectError,
- trio.TooSlowError: ConnectTimeout,
- trio.BrokenResourceError: ConnectError,
- }
-
- with map_exceptions(exc_map):
- with trio.fail_after(connect_timeout):
- stream: trio.abc.Stream = await trio.open_tcp_stream(
- hostname, port, **kwargs
- )
-
- if ssl_context is not None:
- stream = trio.SSLStream(
- stream, ssl_context, server_hostname=hostname.decode("ascii")
- )
- await stream.do_handshake()
-
- return SocketStream(stream=stream)
-
- async def open_uds_stream(
- self,
- path: str,
- hostname: bytes,
- ssl_context: Optional[SSLContext],
- timeout: TimeoutDict,
- ) -> AsyncSocketStream:
- connect_timeout = none_as_inf(timeout.get("connect"))
- exc_map = {
- OSError: ConnectError,
- trio.TooSlowError: ConnectTimeout,
- trio.BrokenResourceError: ConnectError,
- }
-
- with map_exceptions(exc_map):
- with trio.fail_after(connect_timeout):
- stream: trio.abc.Stream = await trio.open_unix_socket(path)
-
- if ssl_context is not None:
- stream = trio.SSLStream(
- stream, ssl_context, server_hostname=hostname.decode("ascii")
- )
- await stream.do_handshake()
-
- return SocketStream(stream=stream)
-
- def create_lock(self) -> AsyncLock:
- return Lock()
-
- def create_semaphore(self, max_value: int, exc_class: type) -> AsyncSemaphore:
- return Semaphore(max_value, exc_class=exc_class)
-
- async def time(self) -> float:
- return trio.current_time()
-
- async def sleep(self, seconds: float) -> None:
- await trio.sleep(seconds)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_bytestreams.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_bytestreams.py
deleted file mode 100644
index 317f4110..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_bytestreams.py
+++ /dev/null
@@ -1,96 +0,0 @@
-from typing import AsyncIterator, Callable, Iterator
-
-from ._async.base import AsyncByteStream
-from ._sync.base import SyncByteStream
-
-
-class ByteStream(AsyncByteStream, SyncByteStream):
- """
- A concrete implementation for either sync or async byte streams.
-
- Example::
-
- stream = httpcore.ByteStream(b"123")
-
- Parameters
- ----------
- content:
- A plain byte string used as the content of the stream.
- """
-
- def __init__(self, content: bytes) -> None:
- self._content = content
-
- def __iter__(self) -> Iterator[bytes]:
- yield self._content
-
- async def __aiter__(self) -> AsyncIterator[bytes]:
- yield self._content
-
-
-class IteratorByteStream(SyncByteStream):
- """
- A concrete implementation for sync byte streams.
-
- Example::
-
- def generate_content():
- yield b"Hello, world!"
- ...
-
- stream = httpcore.IteratorByteStream(generate_content())
-
- Parameters
- ----------
- iterator:
- A sync byte iterator, used as the content of the stream.
- close_func:
- An optional function called when closing the stream.
- """
-
- def __init__(self, iterator: Iterator[bytes], close_func: Callable = None) -> None:
- self._iterator = iterator
- self._close_func = close_func
-
- def __iter__(self) -> Iterator[bytes]:
- for chunk in self._iterator:
- yield chunk
-
- def close(self) -> None:
- if self._close_func is not None:
- self._close_func()
-
-
-class AsyncIteratorByteStream(AsyncByteStream):
- """
- A concrete implementation for async byte streams.
-
- Example::
-
- async def generate_content():
- yield b"Hello, world!"
- ...
-
- stream = httpcore.AsyncIteratorByteStream(generate_content())
-
- Parameters
- ----------
- aiterator:
- An async byte iterator, used as the content of the stream.
- aclose_func:
- An optional async function called when closing the stream.
- """
-
- def __init__(
- self, aiterator: AsyncIterator[bytes], aclose_func: Callable = None
- ) -> None:
- self._aiterator = aiterator
- self._aclose_func = aclose_func
-
- async def __aiter__(self) -> AsyncIterator[bytes]:
- async for chunk in self._aiterator:
- yield chunk
-
- async def aclose(self) -> None:
- if self._aclose_func is not None:
- await self._aclose_func()
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_exceptions.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_exceptions.py
deleted file mode 100644
index ba568299..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_exceptions.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import contextlib
-from typing import Dict, Iterator, Type
-
-
-@contextlib.contextmanager
-def map_exceptions(map: Dict[Type[Exception], Type[Exception]]) -> Iterator[None]:
- try:
- yield
- except Exception as exc: # noqa: PIE786
- for from_exc, to_exc in map.items():
- if isinstance(exc, from_exc):
- raise to_exc(exc) from None
- raise
-
-
-class UnsupportedProtocol(Exception):
- pass
-
-
-class ProtocolError(Exception):
- pass
-
-
-class RemoteProtocolError(ProtocolError):
- pass
-
-
-class LocalProtocolError(ProtocolError):
- pass
-
-
-class ProxyError(Exception):
- pass
-
-
-# Timeout errors
-
-
-class TimeoutException(Exception):
- pass
-
-
-class PoolTimeout(TimeoutException):
- pass
-
-
-class ConnectTimeout(TimeoutException):
- pass
-
-
-class ReadTimeout(TimeoutException):
- pass
-
-
-class WriteTimeout(TimeoutException):
- pass
-
-
-# Network errors
-
-
-class NetworkError(Exception):
- pass
-
-
-class ConnectError(NetworkError):
- pass
-
-
-class ReadError(NetworkError):
- pass
-
-
-class WriteError(NetworkError):
- pass
-
-
-class CloseError(NetworkError):
- pass
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__init__.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/__init__.cpython-39.pyc
deleted file mode 100644
index 4fbf7f3b..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/__init__.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/base.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/base.cpython-39.pyc
deleted file mode 100644
index 6614e7d2..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/base.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection.cpython-39.pyc
deleted file mode 100644
index 4b362cb8..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-39.pyc
deleted file mode 100644
index bd797799..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/connection_pool.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http.cpython-39.pyc
deleted file mode 100644
index c93b93d5..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http11.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http11.cpython-39.pyc
deleted file mode 100644
index 4bd2e49c..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http11.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http2.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http2.cpython-39.pyc
deleted file mode 100644
index 626c5503..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http2.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-39.pyc
deleted file mode 100644
index 3f441d2c..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/__pycache__/http_proxy.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/base.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/base.py
deleted file mode 100644
index 45ef4abf..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/base.py
+++ /dev/null
@@ -1,122 +0,0 @@
-import enum
-from types import TracebackType
-from typing import Iterator, Tuple, Type
-
-from .._types import URL, Headers, T
-
-
-class NewConnectionRequired(Exception):
- pass
-
-
-class ConnectionState(enum.IntEnum):
- """
- PENDING READY
- | | ^
- v V |
- ACTIVE |
- | | |
- | V |
- V IDLE-+
- FULL |
- | |
- V V
- CLOSED
- """
-
- PENDING = 0 # Connection not yet acquired.
- READY = 1 # Re-acquired from pool, about to send a request.
- ACTIVE = 2 # Active requests.
- FULL = 3 # Active requests, no more stream IDs available.
- IDLE = 4 # No active requests.
- CLOSED = 5 # Connection closed.
-
-
-class SyncByteStream:
- """
- The base interface for request and response bodies.
-
- Concrete implementations should subclass this class, and implement
- the :meth:`__iter__` method, and optionally the :meth:`close` method.
- """
-
- def __iter__(self) -> Iterator[bytes]:
- """
- Yield bytes representing the request or response body.
- """
- yield b"" # pragma: nocover
-
- def close(self) -> None:
- """
- Must be called by the client to indicate that the stream has been closed.
- """
- pass # pragma: nocover
-
- def read(self) -> bytes:
- try:
- return b"".join([part for part in self])
- finally:
- self.close()
-
-
-class SyncHTTPTransport:
- """
- The base interface for sending HTTP requests.
-
- Concrete implementations should subclass this class, and implement
- the :meth:`handle_request` method, and optionally the :meth:`close` method.
- """
-
- def handle_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: SyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, SyncByteStream, dict]:
- """
- The interface for sending a single HTTP request, and returning a response.
-
- Parameters
- ----------
- method:
- The HTTP method, such as ``b'GET'``.
- url:
- The URL as a 4-tuple of (scheme, host, port, path).
- headers:
- Any HTTP headers to send with the request.
- stream:
- The body of the HTTP request.
- extensions:
- A dictionary of optional extensions.
-
- Returns
- -------
- status_code:
- The HTTP status code, such as ``200``.
- headers:
- Any HTTP headers included on the response.
- stream:
- The body of the HTTP response.
- extensions:
- A dictionary of optional extensions.
- """
- raise NotImplementedError() # pragma: nocover
-
- def close(self) -> None:
- """
- Close the implementation, which should close any outstanding response streams,
- and any keep alive connections.
- """
-
- def __enter__(self: T) -> T:
- return self
-
- def __exit__(
- self,
- exc_type: Type[BaseException] = None,
- exc_value: BaseException = None,
- traceback: TracebackType = None,
- ) -> None:
- self.close()
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/connection.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/connection.py
deleted file mode 100644
index 382a4f9f..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/connection.py
+++ /dev/null
@@ -1,220 +0,0 @@
-from ssl import SSLContext
-from typing import List, Optional, Tuple, cast
-
-from .._backends.sync import SyncBackend, SyncLock, SyncSocketStream, SyncBackend
-from .._exceptions import ConnectError, ConnectTimeout
-from .._types import URL, Headers, Origin, TimeoutDict
-from .._utils import exponential_backoff, get_logger, url_to_origin
-from .base import SyncByteStream, SyncHTTPTransport, NewConnectionRequired
-from .http import SyncBaseHTTPConnection
-from .http11 import SyncHTTP11Connection
-
-logger = get_logger(__name__)
-
-RETRIES_BACKOFF_FACTOR = 0.5 # 0s, 0.5s, 1s, 2s, 4s, etc.
-
-
-class SyncHTTPConnection(SyncHTTPTransport):
- def __init__(
- self,
- origin: Origin,
- http1: bool = True,
- http2: bool = False,
- keepalive_expiry: float = None,
- uds: str = None,
- ssl_context: SSLContext = None,
- socket: SyncSocketStream = None,
- local_address: str = None,
- retries: int = 0,
- backend: SyncBackend = None,
- ):
- self.origin = origin
- self._http1_enabled = http1
- self._http2_enabled = http2
- self._keepalive_expiry = keepalive_expiry
- self._uds = uds
- self._ssl_context = SSLContext() if ssl_context is None else ssl_context
- self.socket = socket
- self._local_address = local_address
- self._retries = retries
-
- alpn_protocols: List[str] = []
- if http1:
- alpn_protocols.append("http/1.1")
- if http2:
- alpn_protocols.append("h2")
-
- self._ssl_context.set_alpn_protocols(alpn_protocols)
-
- self.connection: Optional[SyncBaseHTTPConnection] = None
- self._is_http11 = False
- self._is_http2 = False
- self._connect_failed = False
- self._expires_at: Optional[float] = None
- self._backend = SyncBackend() if backend is None else backend
-
- def __repr__(self) -> str:
- return f""
-
- def info(self) -> str:
- if self.connection is None:
- return "Connection failed" if self._connect_failed else "Connecting"
- return self.connection.info()
-
- def should_close(self) -> bool:
- """
- Return `True` if the connection is in a state where it should be closed.
- This occurs when any of the following occur:
-
- * There are no active requests on an HTTP/1.1 connection, and the underlying
- socket is readable. The only valid state the socket can be readable in
- if this occurs is when the b"" EOF marker is about to be returned,
- indicating a server disconnect.
- * There are no active requests being made and the keepalive timeout has passed.
- """
- if self.connection is None:
- return False
- return self.connection.should_close()
-
- def is_idle(self) -> bool:
- """
- Return `True` if the connection is currently idle.
- """
- if self.connection is None:
- return False
- return self.connection.is_idle()
-
- def is_closed(self) -> bool:
- if self.connection is None:
- return self._connect_failed
- return self.connection.is_closed()
-
- def is_available(self) -> bool:
- """
- Return `True` if the connection is currently able to accept an outgoing request.
- This occurs when any of the following occur:
-
- * The connection has not yet been opened, and HTTP/2 support is enabled.
- We don't *know* at this point if we'll end up on an HTTP/2 connection or
- not, but we *might* do, so we indicate availability.
- * The connection has been opened, and is currently idle.
- * The connection is open, and is an HTTP/2 connection. The connection must
- also not currently be exceeding the maximum number of allowable concurrent
- streams and must not have exhausted the maximum total number of stream IDs.
- """
- if self.connection is None:
- return self._http2_enabled and not self.is_closed
- return self.connection.is_available()
-
- @property
- def request_lock(self) -> SyncLock:
- # We do this lazily, to make sure backend autodetection always
- # runs within an async context.
- if not hasattr(self, "_request_lock"):
- self._request_lock = self._backend.create_lock()
- return self._request_lock
-
- def handle_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: SyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, SyncByteStream, dict]:
- assert url_to_origin(url) == self.origin
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
-
- with self.request_lock:
- if self.connection is None:
- if self._connect_failed:
- raise NewConnectionRequired()
- if not self.socket:
- logger.trace(
- "open_socket origin=%r timeout=%r", self.origin, timeout
- )
- self.socket = self._open_socket(timeout)
- self._create_connection(self.socket)
- elif not self.connection.is_available():
- raise NewConnectionRequired()
-
- assert self.connection is not None
- logger.trace(
- "connection.handle_request method=%r url=%r headers=%r",
- method,
- url,
- headers,
- )
- return self.connection.handle_request(
- method, url, headers, stream, extensions
- )
-
- def _open_socket(self, timeout: TimeoutDict = None) -> SyncSocketStream:
- scheme, hostname, port = self.origin
- timeout = {} if timeout is None else timeout
- ssl_context = self._ssl_context if scheme == b"https" else None
-
- retries_left = self._retries
- delays = exponential_backoff(factor=RETRIES_BACKOFF_FACTOR)
-
- while True:
- try:
- if self._uds is None:
- return self._backend.open_tcp_stream(
- hostname,
- port,
- ssl_context,
- timeout,
- local_address=self._local_address,
- )
- else:
- return self._backend.open_uds_stream(
- self._uds, hostname, ssl_context, timeout
- )
- except (ConnectError, ConnectTimeout):
- if retries_left <= 0:
- self._connect_failed = True
- raise
- retries_left -= 1
- delay = next(delays)
- self._backend.sleep(delay)
- except Exception: # noqa: PIE786
- self._connect_failed = True
- raise
-
- def _create_connection(self, socket: SyncSocketStream) -> None:
- http_version = socket.get_http_version()
- logger.trace(
- "create_connection socket=%r http_version=%r", socket, http_version
- )
- if http_version == "HTTP/2" or (
- self._http2_enabled and not self._http1_enabled
- ):
- from .http2 import SyncHTTP2Connection
-
- self._is_http2 = True
- self.connection = SyncHTTP2Connection(
- socket=socket,
- keepalive_expiry=self._keepalive_expiry,
- backend=self._backend,
- )
- else:
- self._is_http11 = True
- self.connection = SyncHTTP11Connection(
- socket=socket, keepalive_expiry=self._keepalive_expiry
- )
-
- def start_tls(
- self, hostname: bytes, ssl_context: SSLContext, timeout: TimeoutDict = None
- ) -> None:
- if self.connection is not None:
- logger.trace("start_tls hostname=%r timeout=%r", hostname, timeout)
- self.socket = self.connection.start_tls(
- hostname, ssl_context, timeout
- )
- logger.trace("start_tls complete hostname=%r timeout=%r", hostname, timeout)
-
- def close(self) -> None:
- with self.request_lock:
- if self.connection is not None:
- self.connection.close()
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/connection_pool.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/connection_pool.py
deleted file mode 100644
index 0bd759db..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/connection_pool.py
+++ /dev/null
@@ -1,362 +0,0 @@
-import warnings
-from ssl import SSLContext
-from typing import (
- Iterator,
- Callable,
- Dict,
- List,
- Optional,
- Set,
- Tuple,
- Union,
- cast,
-)
-
-from .._backends.sync import SyncBackend, SyncLock, SyncSemaphore
-from .._backends.base import lookup_sync_backend
-from .._exceptions import LocalProtocolError, PoolTimeout, UnsupportedProtocol
-from .._threadlock import ThreadLock
-from .._types import URL, Headers, Origin, TimeoutDict
-from .._utils import get_logger, origin_to_url_string, url_to_origin
-from .base import SyncByteStream, SyncHTTPTransport, NewConnectionRequired
-from .connection import SyncHTTPConnection
-
-logger = get_logger(__name__)
-
-
-class NullSemaphore(SyncSemaphore):
- def __init__(self) -> None:
- pass
-
- def acquire(self, timeout: float = None) -> None:
- return
-
- def release(self) -> None:
- return
-
-
-class ResponseByteStream(SyncByteStream):
- def __init__(
- self,
- stream: SyncByteStream,
- connection: SyncHTTPConnection,
- callback: Callable,
- ) -> None:
- """
- A wrapper around the response stream that we return from
- `.handle_request()`.
-
- Ensures that when `stream.close()` is called, the connection pool
- is notified via a callback.
- """
- self.stream = stream
- self.connection = connection
- self.callback = callback
-
- def __iter__(self) -> Iterator[bytes]:
- for chunk in self.stream:
- yield chunk
-
- def close(self) -> None:
- try:
- # Call the underlying stream close callback.
- # This will be a call to `SyncHTTP11Connection._response_closed()`
- # or `SyncHTTP2Stream._response_closed()`.
- self.stream.close()
- finally:
- # Call the connection pool close callback.
- # This will be a call to `SyncConnectionPool._response_closed()`.
- self.callback(self.connection)
-
-
-class SyncConnectionPool(SyncHTTPTransport):
- """
- A connection pool for making HTTP requests.
-
- Parameters
- ----------
- ssl_context:
- An SSL context to use for verifying connections.
- max_connections:
- The maximum number of concurrent connections to allow.
- max_keepalive_connections:
- The maximum number of connections to allow before closing keep-alive
- connections.
- keepalive_expiry:
- The maximum time to allow before closing a keep-alive connection.
- http1:
- Enable/Disable HTTP/1.1 support. Defaults to True.
- http2:
- Enable/Disable HTTP/2 support. Defaults to False.
- uds:
- Path to a Unix Domain Socket to use instead of TCP sockets.
- local_address:
- Local address to connect from. Can also be used to connect using a particular
- address family. Using ``local_address="0.0.0.0"`` will connect using an
- ``AF_INET`` address (IPv4), while using ``local_address="::"`` will connect
- using an ``AF_INET6`` address (IPv6).
- retries:
- The maximum number of retries when trying to establish a connection.
- backend:
- A name indicating which concurrency backend to use.
- """
-
- def __init__(
- self,
- ssl_context: SSLContext = None,
- max_connections: int = None,
- max_keepalive_connections: int = None,
- keepalive_expiry: float = None,
- http1: bool = True,
- http2: bool = False,
- uds: str = None,
- local_address: str = None,
- retries: int = 0,
- max_keepalive: int = None,
- backend: Union[SyncBackend, str] = "sync",
- ):
- if max_keepalive is not None:
- warnings.warn(
- "'max_keepalive' is deprecated. Use 'max_keepalive_connections'.",
- DeprecationWarning,
- )
- max_keepalive_connections = max_keepalive
-
- if isinstance(backend, str):
- backend = lookup_sync_backend(backend)
-
- self._ssl_context = SSLContext() if ssl_context is None else ssl_context
- self._max_connections = max_connections
- self._max_keepalive_connections = max_keepalive_connections
- self._keepalive_expiry = keepalive_expiry
- self._http1 = http1
- self._http2 = http2
- self._uds = uds
- self._local_address = local_address
- self._retries = retries
- self._connections: Dict[Origin, Set[SyncHTTPConnection]] = {}
- self._thread_lock = ThreadLock()
- self._backend = backend
- self._next_keepalive_check = 0.0
-
- if not (http1 or http2):
- raise ValueError("Either http1 or http2 must be True.")
-
- if http2:
- try:
- import h2 # noqa: F401
- except ImportError:
- raise ImportError(
- "Attempted to use http2=True, but the 'h2' "
- "package is not installed. Use 'pip install httpcore[http2]'."
- )
-
- @property
- def _connection_semaphore(self) -> SyncSemaphore:
- # We do this lazily, to make sure backend autodetection always
- # runs within an async context.
- if not hasattr(self, "_internal_semaphore"):
- if self._max_connections is not None:
- self._internal_semaphore = self._backend.create_semaphore(
- self._max_connections, exc_class=PoolTimeout
- )
- else:
- self._internal_semaphore = NullSemaphore()
-
- return self._internal_semaphore
-
- @property
- def _connection_acquiry_lock(self) -> SyncLock:
- if not hasattr(self, "_internal_connection_acquiry_lock"):
- self._internal_connection_acquiry_lock = self._backend.create_lock()
- return self._internal_connection_acquiry_lock
-
- def _create_connection(
- self,
- origin: Tuple[bytes, bytes, int],
- ) -> SyncHTTPConnection:
- return SyncHTTPConnection(
- origin=origin,
- http1=self._http1,
- http2=self._http2,
- keepalive_expiry=self._keepalive_expiry,
- uds=self._uds,
- ssl_context=self._ssl_context,
- local_address=self._local_address,
- retries=self._retries,
- backend=self._backend,
- )
-
- def handle_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: SyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, SyncByteStream, dict]:
- if not url[0]:
- raise UnsupportedProtocol(
- "Request URL missing either an 'http://' or 'https://' protocol."
- )
-
- if url[0] not in (b"http", b"https"):
- protocol = url[0].decode("ascii")
- raise UnsupportedProtocol(
- f"Request URL has an unsupported protocol '{protocol}://'."
- )
-
- if not url[1]:
- raise LocalProtocolError("Missing hostname in URL.")
-
- origin = url_to_origin(url)
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
-
- self._keepalive_sweep()
-
- connection: Optional[SyncHTTPConnection] = None
- while connection is None:
- with self._connection_acquiry_lock:
- # We get-or-create a connection as an atomic operation, to ensure
- # that HTTP/2 requests issued in close concurrency will end up
- # on the same connection.
- logger.trace("get_connection_from_pool=%r", origin)
- connection = self._get_connection_from_pool(origin)
-
- if connection is None:
- connection = self._create_connection(origin=origin)
- logger.trace("created connection=%r", connection)
- self._add_to_pool(connection, timeout=timeout)
- else:
- logger.trace("reuse connection=%r", connection)
-
- try:
- response = connection.handle_request(
- method, url, headers=headers, stream=stream, extensions=extensions
- )
- except NewConnectionRequired:
- connection = None
- except BaseException: # noqa: PIE786
- # See https://github.com/encode/httpcore/pull/305 for motivation
- # behind catching 'BaseException' rather than 'Exception' here.
- logger.trace("remove from pool connection=%r", connection)
- self._remove_from_pool(connection)
- raise
-
- status_code, headers, stream, extensions = response
- wrapped_stream = ResponseByteStream(
- stream, connection=connection, callback=self._response_closed
- )
- return status_code, headers, wrapped_stream, extensions
-
- def _get_connection_from_pool(
- self, origin: Origin
- ) -> Optional[SyncHTTPConnection]:
- # Determine expired keep alive connections on this origin.
- reuse_connection = None
- connections_to_close = set()
-
- for connection in self._connections_for_origin(origin):
- if connection.should_close():
- connections_to_close.add(connection)
- self._remove_from_pool(connection)
- elif connection.is_available():
- reuse_connection = connection
-
- # Close any dropped connections.
- for connection in connections_to_close:
- connection.close()
-
- return reuse_connection
-
- def _response_closed(self, connection: SyncHTTPConnection) -> None:
- remove_from_pool = False
- close_connection = False
-
- if connection.is_closed():
- remove_from_pool = True
- elif connection.is_idle():
- num_connections = len(self._get_all_connections())
- if (
- self._max_keepalive_connections is not None
- and num_connections > self._max_keepalive_connections
- ):
- remove_from_pool = True
- close_connection = True
-
- if remove_from_pool:
- self._remove_from_pool(connection)
-
- if close_connection:
- connection.close()
-
- def _keepalive_sweep(self) -> None:
- """
- Remove any IDLE connections that have expired past their keep-alive time.
- """
- if self._keepalive_expiry is None:
- return
-
- now = self._backend.time()
- if now < self._next_keepalive_check:
- return
-
- self._next_keepalive_check = now + min(1.0, self._keepalive_expiry)
- connections_to_close = set()
-
- for connection in self._get_all_connections():
- if connection.should_close():
- connections_to_close.add(connection)
- self._remove_from_pool(connection)
-
- for connection in connections_to_close:
- connection.close()
-
- def _add_to_pool(
- self, connection: SyncHTTPConnection, timeout: TimeoutDict
- ) -> None:
- logger.trace("adding connection to pool=%r", connection)
- self._connection_semaphore.acquire(timeout=timeout.get("pool", None))
- with self._thread_lock:
- self._connections.setdefault(connection.origin, set())
- self._connections[connection.origin].add(connection)
-
- def _remove_from_pool(self, connection: SyncHTTPConnection) -> None:
- logger.trace("removing connection from pool=%r", connection)
- with self._thread_lock:
- if connection in self._connections.get(connection.origin, set()):
- self._connection_semaphore.release()
- self._connections[connection.origin].remove(connection)
- if not self._connections[connection.origin]:
- del self._connections[connection.origin]
-
- def _connections_for_origin(self, origin: Origin) -> Set[SyncHTTPConnection]:
- return set(self._connections.get(origin, set()))
-
- def _get_all_connections(self) -> Set[SyncHTTPConnection]:
- connections: Set[SyncHTTPConnection] = set()
- for connection_set in self._connections.values():
- connections |= connection_set
- return connections
-
- def close(self) -> None:
- connections = self._get_all_connections()
- for connection in connections:
- self._remove_from_pool(connection)
-
- # Close all connections
- for connection in connections:
- connection.close()
-
- def get_connection_info(self) -> Dict[str, List[str]]:
- """
- Returns a dict of origin URLs to a list of summary strings for each connection.
- """
- self._keepalive_sweep()
-
- stats = {}
- for origin, connections in self._connections.items():
- stats[origin_to_url_string(origin)] = sorted(
- [connection.info() for connection in connections]
- )
- return stats
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/http.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/http.py
deleted file mode 100644
index c128a96b..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/http.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from ssl import SSLContext
-
-from .._backends.sync import SyncSocketStream
-from .._types import TimeoutDict
-from .base import SyncHTTPTransport
-
-
-class SyncBaseHTTPConnection(SyncHTTPTransport):
- def info(self) -> str:
- raise NotImplementedError() # pragma: nocover
-
- def should_close(self) -> bool:
- """
- Return `True` if the connection is in a state where it should be closed.
- """
- raise NotImplementedError() # pragma: nocover
-
- def is_idle(self) -> bool:
- """
- Return `True` if the connection is currently idle.
- """
- raise NotImplementedError() # pragma: nocover
-
- def is_closed(self) -> bool:
- """
- Return `True` if the connection has been closed.
- """
- raise NotImplementedError() # pragma: nocover
-
- def is_available(self) -> bool:
- """
- Return `True` if the connection is currently able to accept an outgoing request.
- """
- raise NotImplementedError() # pragma: nocover
-
- def start_tls(
- self, hostname: bytes, ssl_context: SSLContext, timeout: TimeoutDict = None
- ) -> SyncSocketStream:
- """
- Upgrade the underlying socket to TLS.
- """
- raise NotImplementedError() # pragma: nocover
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/http11.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/http11.py
deleted file mode 100644
index 5dbb42e0..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/http11.py
+++ /dev/null
@@ -1,269 +0,0 @@
-import enum
-import time
-from ssl import SSLContext
-from typing import Iterator, List, Optional, Tuple, Union, cast
-
-import h11
-
-from .._backends.sync import SyncSocketStream
-from .._bytestreams import IteratorByteStream
-from .._exceptions import LocalProtocolError, RemoteProtocolError, map_exceptions
-from .._types import URL, Headers, TimeoutDict
-from .._utils import get_logger
-from .base import SyncByteStream, NewConnectionRequired
-from .http import SyncBaseHTTPConnection
-
-H11Event = Union[
- h11.Request,
- h11.Response,
- h11.InformationalResponse,
- h11.Data,
- h11.EndOfMessage,
- h11.ConnectionClosed,
-]
-
-
-class ConnectionState(enum.IntEnum):
- NEW = 0
- ACTIVE = 1
- IDLE = 2
- CLOSED = 3
-
-
-logger = get_logger(__name__)
-
-
-class SyncHTTP11Connection(SyncBaseHTTPConnection):
- READ_NUM_BYTES = 64 * 1024
-
- def __init__(self, socket: SyncSocketStream, keepalive_expiry: float = None):
- self.socket = socket
-
- self._keepalive_expiry: Optional[float] = keepalive_expiry
- self._should_expire_at: Optional[float] = None
- self._h11_state = h11.Connection(our_role=h11.CLIENT)
- self._state = ConnectionState.NEW
-
- def __repr__(self) -> str:
- return f""
-
- def _now(self) -> float:
- return time.monotonic()
-
- def _server_disconnected(self) -> bool:
- """
- Return True if the connection is idle, and the underlying socket is readable.
- The only valid state the socket can be readable here is when the b""
- EOF marker is about to be returned, indicating a server disconnect.
- """
- return self._state == ConnectionState.IDLE and self.socket.is_readable()
-
- def _keepalive_expired(self) -> bool:
- """
- Return True if the connection is idle, and has passed it's keepalive
- expiry time.
- """
- return (
- self._state == ConnectionState.IDLE
- and self._should_expire_at is not None
- and self._now() >= self._should_expire_at
- )
-
- def info(self) -> str:
- return f"HTTP/1.1, {self._state.name}"
-
- def should_close(self) -> bool:
- """
- Return `True` if the connection is in a state where it should be closed.
- """
- return self._server_disconnected() or self._keepalive_expired()
-
- def is_idle(self) -> bool:
- """
- Return `True` if the connection is currently idle.
- """
- return self._state == ConnectionState.IDLE
-
- def is_closed(self) -> bool:
- """
- Return `True` if the connection has been closed.
- """
- return self._state == ConnectionState.CLOSED
-
- def is_available(self) -> bool:
- """
- Return `True` if the connection is currently able to accept an outgoing request.
- """
- return self._state == ConnectionState.IDLE
-
- def handle_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: SyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, SyncByteStream, dict]:
- """
- Send a single HTTP/1.1 request.
-
- Note that there is no kind of task/thread locking at this layer of interface.
- Dealing with locking for concurrency is handled by the `SyncHTTPConnection`.
- """
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
-
- if self._state in (ConnectionState.NEW, ConnectionState.IDLE):
- self._state = ConnectionState.ACTIVE
- self._should_expire_at = None
- else:
- raise NewConnectionRequired()
-
- self._send_request(method, url, headers, timeout)
- self._send_request_body(stream, timeout)
- (
- http_version,
- status_code,
- reason_phrase,
- headers,
- ) = self._receive_response(timeout)
- response_stream = IteratorByteStream(
- iterator=self._receive_response_data(timeout),
- close_func=self._response_closed,
- )
- extensions = {
- "http_version": http_version,
- "reason_phrase": reason_phrase,
- }
- return (status_code, headers, response_stream, extensions)
-
- def start_tls(
- self, hostname: bytes, ssl_context: SSLContext, timeout: TimeoutDict = None
- ) -> SyncSocketStream:
- timeout = {} if timeout is None else timeout
- self.socket = self.socket.start_tls(hostname, ssl_context, timeout)
- return self.socket
-
- def _send_request(
- self, method: bytes, url: URL, headers: Headers, timeout: TimeoutDict
- ) -> None:
- """
- Send the request line and headers.
- """
- logger.trace("send_request method=%r url=%r headers=%s", method, url, headers)
- _scheme, _host, _port, target = url
- with map_exceptions({h11.LocalProtocolError: LocalProtocolError}):
- event = h11.Request(method=method, target=target, headers=headers)
- self._send_event(event, timeout)
-
- def _send_request_body(
- self, stream: SyncByteStream, timeout: TimeoutDict
- ) -> None:
- """
- Send the request body.
- """
- # Send the request body.
- for chunk in stream:
- logger.trace("send_data=Data(<%d bytes>)", len(chunk))
- event = h11.Data(data=chunk)
- self._send_event(event, timeout)
-
- # Finalize sending the request.
- event = h11.EndOfMessage()
- self._send_event(event, timeout)
-
- def _send_event(self, event: H11Event, timeout: TimeoutDict) -> None:
- """
- Send a single `h11` event to the network, waiting for the data to
- drain before returning.
- """
- bytes_to_send = self._h11_state.send(event)
- self.socket.write(bytes_to_send, timeout)
-
- def _receive_response(
- self, timeout: TimeoutDict
- ) -> Tuple[bytes, int, bytes, List[Tuple[bytes, bytes]]]:
- """
- Read the response status and headers from the network.
- """
- while True:
- event = self._receive_event(timeout)
- if isinstance(event, h11.Response):
- break
-
- http_version = b"HTTP/" + event.http_version
-
- # h11 version 0.11+ supports a `raw_items` interface to get the
- # raw header casing, rather than the enforced lowercase headers.
- headers = event.headers.raw_items()
-
- return http_version, event.status_code, event.reason, headers
-
- def _receive_response_data(
- self, timeout: TimeoutDict
- ) -> Iterator[bytes]:
- """
- Read the response data from the network.
- """
- while True:
- event = self._receive_event(timeout)
- if isinstance(event, h11.Data):
- logger.trace("receive_event=Data(<%d bytes>)", len(event.data))
- yield bytes(event.data)
- elif isinstance(event, (h11.EndOfMessage, h11.PAUSED)):
- logger.trace("receive_event=%r", event)
- break
-
- def _receive_event(self, timeout: TimeoutDict) -> H11Event:
- """
- Read a single `h11` event, reading more data from the network if needed.
- """
- while True:
- with map_exceptions({h11.RemoteProtocolError: RemoteProtocolError}):
- event = self._h11_state.next_event()
-
- if event is h11.NEED_DATA:
- data = self.socket.read(self.READ_NUM_BYTES, timeout)
-
- # If we feed this case through h11 we'll raise an exception like:
- #
- # httpcore.RemoteProtocolError: can't handle event type
- # ConnectionClosed when role=SERVER and state=SEND_RESPONSE
- #
- # Which is accurate, but not very informative from an end-user
- # perspective. Instead we handle messaging for this case distinctly.
- if data == b"" and self._h11_state.their_state == h11.SEND_RESPONSE:
- msg = "Server disconnected without sending a response."
- raise RemoteProtocolError(msg)
-
- self._h11_state.receive_data(data)
- else:
- assert event is not h11.NEED_DATA
- break
- return event
-
- def _response_closed(self) -> None:
- logger.trace(
- "response_closed our_state=%r their_state=%r",
- self._h11_state.our_state,
- self._h11_state.their_state,
- )
- if (
- self._h11_state.our_state is h11.DONE
- and self._h11_state.their_state is h11.DONE
- ):
- self._h11_state.start_next_cycle()
- self._state = ConnectionState.IDLE
- if self._keepalive_expiry is not None:
- self._should_expire_at = self._now() + self._keepalive_expiry
- else:
- self.close()
-
- def close(self) -> None:
- if self._state != ConnectionState.CLOSED:
- self._state = ConnectionState.CLOSED
-
- if self._h11_state.our_state is h11.MUST_CLOSE:
- event = h11.ConnectionClosed()
- self._h11_state.send(event)
-
- self.socket.close()
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/http2.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/http2.py
deleted file mode 100644
index 90caf5fa..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/http2.py
+++ /dev/null
@@ -1,446 +0,0 @@
-import enum
-import time
-from ssl import SSLContext
-from typing import Iterator, Dict, List, Optional, Tuple, cast
-
-import h2.connection
-import h2.events
-from h2.config import H2Configuration
-from h2.exceptions import NoAvailableStreamIDError
-from h2.settings import SettingCodes, Settings
-
-from .._backends.sync import SyncBackend, SyncLock, SyncSemaphore, SyncSocketStream
-from .._bytestreams import IteratorByteStream
-from .._exceptions import LocalProtocolError, PoolTimeout, RemoteProtocolError
-from .._types import URL, Headers, TimeoutDict
-from .._utils import get_logger
-from .base import SyncByteStream, NewConnectionRequired
-from .http import SyncBaseHTTPConnection
-
-logger = get_logger(__name__)
-
-
-class ConnectionState(enum.IntEnum):
- IDLE = 0
- ACTIVE = 1
- CLOSED = 2
-
-
-class SyncHTTP2Connection(SyncBaseHTTPConnection):
- READ_NUM_BYTES = 64 * 1024
- CONFIG = H2Configuration(validate_inbound_headers=False)
-
- def __init__(
- self,
- socket: SyncSocketStream,
- backend: SyncBackend,
- keepalive_expiry: float = None,
- ):
- self.socket = socket
-
- self._backend = backend
- self._h2_state = h2.connection.H2Connection(config=self.CONFIG)
-
- self._sent_connection_init = False
- self._streams: Dict[int, SyncHTTP2Stream] = {}
- self._events: Dict[int, List[h2.events.Event]] = {}
-
- self._keepalive_expiry: Optional[float] = keepalive_expiry
- self._should_expire_at: Optional[float] = None
- self._state = ConnectionState.ACTIVE
- self._exhausted_available_stream_ids = False
-
- def __repr__(self) -> str:
- return f""
-
- def info(self) -> str:
- return f"HTTP/2, {self._state.name}, {len(self._streams)} streams"
-
- def _now(self) -> float:
- return time.monotonic()
-
- def should_close(self) -> bool:
- """
- Return `True` if the connection is currently idle, and the keepalive
- timeout has passed.
- """
- return (
- self._state == ConnectionState.IDLE
- and self._should_expire_at is not None
- and self._now() >= self._should_expire_at
- )
-
- def is_idle(self) -> bool:
- """
- Return `True` if the connection is currently idle.
- """
- return self._state == ConnectionState.IDLE
-
- def is_closed(self) -> bool:
- """
- Return `True` if the connection has been closed.
- """
- return self._state == ConnectionState.CLOSED
-
- def is_available(self) -> bool:
- """
- Return `True` if the connection is currently able to accept an outgoing request.
- This occurs when any of the following occur:
-
- * The connection has not yet been opened, and HTTP/2 support is enabled.
- We don't *know* at this point if we'll end up on an HTTP/2 connection or
- not, but we *might* do, so we indicate availability.
- * The connection has been opened, and is currently idle.
- * The connection is open, and is an HTTP/2 connection. The connection must
- also not have exhausted the maximum total number of stream IDs.
- """
- return (
- self._state != ConnectionState.CLOSED
- and not self._exhausted_available_stream_ids
- )
-
- @property
- def init_lock(self) -> SyncLock:
- # We do this lazily, to make sure backend autodetection always
- # runs within an async context.
- if not hasattr(self, "_initialization_lock"):
- self._initialization_lock = self._backend.create_lock()
- return self._initialization_lock
-
- @property
- def read_lock(self) -> SyncLock:
- # We do this lazily, to make sure backend autodetection always
- # runs within an async context.
- if not hasattr(self, "_read_lock"):
- self._read_lock = self._backend.create_lock()
- return self._read_lock
-
- @property
- def max_streams_semaphore(self) -> SyncSemaphore:
- # We do this lazily, to make sure backend autodetection always
- # runs within an async context.
- if not hasattr(self, "_max_streams_semaphore"):
- max_streams = self._h2_state.local_settings.max_concurrent_streams
- self._max_streams_semaphore = self._backend.create_semaphore(
- max_streams, exc_class=PoolTimeout
- )
- return self._max_streams_semaphore
-
- def start_tls(
- self, hostname: bytes, ssl_context: SSLContext, timeout: TimeoutDict = None
- ) -> SyncSocketStream:
- raise NotImplementedError("TLS upgrade not supported on HTTP/2 connections.")
-
- def handle_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: SyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, SyncByteStream, dict]:
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
-
- with self.init_lock:
- if not self._sent_connection_init:
- # The very first stream is responsible for initiating the connection.
- self._state = ConnectionState.ACTIVE
- self.send_connection_init(timeout)
- self._sent_connection_init = True
-
- self.max_streams_semaphore.acquire()
- try:
- try:
- stream_id = self._h2_state.get_next_available_stream_id()
- except NoAvailableStreamIDError:
- self._exhausted_available_stream_ids = True
- raise NewConnectionRequired()
- else:
- self._state = ConnectionState.ACTIVE
- self._should_expire_at = None
-
- h2_stream = SyncHTTP2Stream(stream_id=stream_id, connection=self)
- self._streams[stream_id] = h2_stream
- self._events[stream_id] = []
- return h2_stream.handle_request(
- method, url, headers, stream, extensions
- )
- except Exception: # noqa: PIE786
- self.max_streams_semaphore.release()
- raise
-
- def send_connection_init(self, timeout: TimeoutDict) -> None:
- """
- The HTTP/2 connection requires some initial setup before we can start
- using individual request/response streams on it.
- """
- # Need to set these manually here instead of manipulating via
- # __setitem__() otherwise the H2Connection will emit SettingsUpdate
- # frames in addition to sending the undesired defaults.
- self._h2_state.local_settings = Settings(
- client=True,
- initial_values={
- # Disable PUSH_PROMISE frames from the server since we don't do anything
- # with them for now. Maybe when we support caching?
- SettingCodes.ENABLE_PUSH: 0,
- # These two are taken from h2 for safe defaults
- SettingCodes.MAX_CONCURRENT_STREAMS: 100,
- SettingCodes.MAX_HEADER_LIST_SIZE: 65536,
- },
- )
-
- # Some websites (*cough* Yahoo *cough*) balk at this setting being
- # present in the initial handshake since it's not defined in the original
- # RFC despite the RFC mandating ignoring settings you don't know about.
- del self._h2_state.local_settings[
- h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL
- ]
-
- logger.trace("initiate_connection=%r", self)
- self._h2_state.initiate_connection()
- self._h2_state.increment_flow_control_window(2 ** 24)
- data_to_send = self._h2_state.data_to_send()
- self.socket.write(data_to_send, timeout)
-
- def is_socket_readable(self) -> bool:
- return self.socket.is_readable()
-
- def close(self) -> None:
- logger.trace("close_connection=%r", self)
- if self._state != ConnectionState.CLOSED:
- self._state = ConnectionState.CLOSED
-
- self.socket.close()
-
- def wait_for_outgoing_flow(self, stream_id: int, timeout: TimeoutDict) -> int:
- """
- Returns the maximum allowable outgoing flow for a given stream.
- If the allowable flow is zero, then waits on the network until
- WindowUpdated frames have increased the flow rate.
- https://tools.ietf.org/html/rfc7540#section-6.9
- """
- local_flow = self._h2_state.local_flow_control_window(stream_id)
- connection_flow = self._h2_state.max_outbound_frame_size
- flow = min(local_flow, connection_flow)
- while flow == 0:
- self.receive_events(timeout)
- local_flow = self._h2_state.local_flow_control_window(stream_id)
- connection_flow = self._h2_state.max_outbound_frame_size
- flow = min(local_flow, connection_flow)
- return flow
-
- def wait_for_event(
- self, stream_id: int, timeout: TimeoutDict
- ) -> h2.events.Event:
- """
- Returns the next event for a given stream.
- If no events are available yet, then waits on the network until
- an event is available.
- """
- with self.read_lock:
- while not self._events[stream_id]:
- self.receive_events(timeout)
- return self._events[stream_id].pop(0)
-
- def receive_events(self, timeout: TimeoutDict) -> None:
- """
- Read some data from the network, and update the H2 state.
- """
- data = self.socket.read(self.READ_NUM_BYTES, timeout)
- if data == b"":
- raise RemoteProtocolError("Server disconnected")
-
- events = self._h2_state.receive_data(data)
- for event in events:
- event_stream_id = getattr(event, "stream_id", 0)
- logger.trace("receive_event stream_id=%r event=%s", event_stream_id, event)
-
- if hasattr(event, "error_code"):
- raise RemoteProtocolError(event)
-
- if event_stream_id in self._events:
- self._events[event_stream_id].append(event)
-
- data_to_send = self._h2_state.data_to_send()
- self.socket.write(data_to_send, timeout)
-
- def send_headers(
- self, stream_id: int, headers: Headers, end_stream: bool, timeout: TimeoutDict
- ) -> None:
- logger.trace("send_headers stream_id=%r headers=%r", stream_id, headers)
- self._h2_state.send_headers(stream_id, headers, end_stream=end_stream)
- self._h2_state.increment_flow_control_window(2 ** 24, stream_id=stream_id)
- data_to_send = self._h2_state.data_to_send()
- self.socket.write(data_to_send, timeout)
-
- def send_data(
- self, stream_id: int, chunk: bytes, timeout: TimeoutDict
- ) -> None:
- logger.trace("send_data stream_id=%r chunk=%r", stream_id, chunk)
- self._h2_state.send_data(stream_id, chunk)
- data_to_send = self._h2_state.data_to_send()
- self.socket.write(data_to_send, timeout)
-
- def end_stream(self, stream_id: int, timeout: TimeoutDict) -> None:
- logger.trace("end_stream stream_id=%r", stream_id)
- self._h2_state.end_stream(stream_id)
- data_to_send = self._h2_state.data_to_send()
- self.socket.write(data_to_send, timeout)
-
- def acknowledge_received_data(
- self, stream_id: int, amount: int, timeout: TimeoutDict
- ) -> None:
- self._h2_state.acknowledge_received_data(amount, stream_id)
- data_to_send = self._h2_state.data_to_send()
- self.socket.write(data_to_send, timeout)
-
- def close_stream(self, stream_id: int) -> None:
- try:
- logger.trace("close_stream stream_id=%r", stream_id)
- del self._streams[stream_id]
- del self._events[stream_id]
-
- if not self._streams:
- if self._state == ConnectionState.ACTIVE:
- if self._exhausted_available_stream_ids:
- self.close()
- else:
- self._state = ConnectionState.IDLE
- if self._keepalive_expiry is not None:
- self._should_expire_at = (
- self._now() + self._keepalive_expiry
- )
- finally:
- self.max_streams_semaphore.release()
-
-
-class SyncHTTP2Stream:
- def __init__(self, stream_id: int, connection: SyncHTTP2Connection) -> None:
- self.stream_id = stream_id
- self.connection = connection
-
- def handle_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: SyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, SyncByteStream, dict]:
- headers = [(k.lower(), v) for (k, v) in headers]
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
-
- # Send the request.
- seen_headers = set(key for key, value in headers)
- has_body = (
- b"content-length" in seen_headers or b"transfer-encoding" in seen_headers
- )
-
- self.send_headers(method, url, headers, has_body, timeout)
- if has_body:
- self.send_body(stream, timeout)
-
- # Receive the response.
- status_code, headers = self.receive_response(timeout)
- response_stream = IteratorByteStream(
- iterator=self.body_iter(timeout), close_func=self._response_closed
- )
-
- extensions = {
- "http_version": b"HTTP/2",
- }
- return (status_code, headers, response_stream, extensions)
-
- def send_headers(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- has_body: bool,
- timeout: TimeoutDict,
- ) -> None:
- scheme, hostname, port, path = url
-
- # In HTTP/2 the ':authority' pseudo-header is used instead of 'Host'.
- # In order to gracefully handle HTTP/1.1 and HTTP/2 we always require
- # HTTP/1.1 style headers, and map them appropriately if we end up on
- # an HTTP/2 connection.
- authority = None
-
- for k, v in headers:
- if k == b"host":
- authority = v
- break
-
- if authority is None:
- # Mirror the same error we'd see with `h11`, so that the behaviour
- # is consistent. Although we're dealing with an `:authority`
- # pseudo-header by this point, from an end-user perspective the issue
- # is that the outgoing request needed to include a `host` header.
- raise LocalProtocolError("Missing mandatory Host: header")
-
- headers = [
- (b":method", method),
- (b":authority", authority),
- (b":scheme", scheme),
- (b":path", path),
- ] + [
- (k, v)
- for k, v in headers
- if k
- not in (
- b"host",
- b"transfer-encoding",
- )
- ]
- end_stream = not has_body
-
- self.connection.send_headers(self.stream_id, headers, end_stream, timeout)
-
- def send_body(self, stream: SyncByteStream, timeout: TimeoutDict) -> None:
- for data in stream:
- while data:
- max_flow = self.connection.wait_for_outgoing_flow(
- self.stream_id, timeout
- )
- chunk_size = min(len(data), max_flow)
- chunk, data = data[:chunk_size], data[chunk_size:]
- self.connection.send_data(self.stream_id, chunk, timeout)
-
- self.connection.end_stream(self.stream_id, timeout)
-
- def receive_response(
- self, timeout: TimeoutDict
- ) -> Tuple[int, List[Tuple[bytes, bytes]]]:
- """
- Read the response status and headers from the network.
- """
- while True:
- event = self.connection.wait_for_event(self.stream_id, timeout)
- if isinstance(event, h2.events.ResponseReceived):
- break
-
- status_code = 200
- headers = []
- for k, v in event.headers:
- if k == b":status":
- status_code = int(v.decode("ascii", errors="ignore"))
- elif not k.startswith(b":"):
- headers.append((k, v))
-
- return (status_code, headers)
-
- def body_iter(self, timeout: TimeoutDict) -> Iterator[bytes]:
- while True:
- event = self.connection.wait_for_event(self.stream_id, timeout)
- if isinstance(event, h2.events.DataReceived):
- amount = event.flow_controlled_length
- self.connection.acknowledge_received_data(
- self.stream_id, amount, timeout
- )
- yield event.data
- elif isinstance(event, (h2.events.StreamEnded, h2.events.StreamReset)):
- break
-
- def _response_closed(self) -> None:
- self.connection.close_stream(self.stream_id)
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/http_proxy.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/http_proxy.py
deleted file mode 100644
index 78c02e29..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_sync/http_proxy.py
+++ /dev/null
@@ -1,290 +0,0 @@
-from http import HTTPStatus
-from ssl import SSLContext
-from typing import Tuple, cast
-
-from .._bytestreams import ByteStream
-from .._exceptions import ProxyError
-from .._types import URL, Headers, TimeoutDict
-from .._utils import get_logger, url_to_origin
-from .base import SyncByteStream
-from .connection import SyncHTTPConnection
-from .connection_pool import SyncConnectionPool, ResponseByteStream
-
-logger = get_logger(__name__)
-
-
-def get_reason_phrase(status_code: int) -> str:
- try:
- return HTTPStatus(status_code).phrase
- except ValueError:
- return ""
-
-
-def merge_headers(
- default_headers: Headers = None, override_headers: Headers = None
-) -> Headers:
- """
- Append default_headers and override_headers, de-duplicating if a key existing in
- both cases.
- """
- default_headers = [] if default_headers is None else default_headers
- override_headers = [] if override_headers is None else override_headers
- has_override = set([key.lower() for key, value in override_headers])
- default_headers = [
- (key, value)
- for key, value in default_headers
- if key.lower() not in has_override
- ]
- return default_headers + override_headers
-
-
-class SyncHTTPProxy(SyncConnectionPool):
- """
- A connection pool for making HTTP requests via an HTTP proxy.
-
- Parameters
- ----------
- proxy_url:
- The URL of the proxy service as a 4-tuple of (scheme, host, port, path).
- proxy_headers:
- A list of proxy headers to include.
- proxy_mode:
- A proxy mode to operate in. May be "DEFAULT", "FORWARD_ONLY", or "TUNNEL_ONLY".
- ssl_context:
- An SSL context to use for verifying connections.
- max_connections:
- The maximum number of concurrent connections to allow.
- max_keepalive_connections:
- The maximum number of connections to allow before closing keep-alive
- connections.
- http2:
- Enable HTTP/2 support.
- """
-
- def __init__(
- self,
- proxy_url: URL,
- proxy_headers: Headers = None,
- proxy_mode: str = "DEFAULT",
- ssl_context: SSLContext = None,
- max_connections: int = None,
- max_keepalive_connections: int = None,
- keepalive_expiry: float = None,
- http2: bool = False,
- backend: str = "sync",
- # Deprecated argument style:
- max_keepalive: int = None,
- ):
- assert proxy_mode in ("DEFAULT", "FORWARD_ONLY", "TUNNEL_ONLY")
-
- self.proxy_origin = url_to_origin(proxy_url)
- self.proxy_headers = [] if proxy_headers is None else proxy_headers
- self.proxy_mode = proxy_mode
- super().__init__(
- ssl_context=ssl_context,
- max_connections=max_connections,
- max_keepalive_connections=max_keepalive_connections,
- keepalive_expiry=keepalive_expiry,
- http2=http2,
- backend=backend,
- max_keepalive=max_keepalive,
- )
-
- def handle_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: SyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, SyncByteStream, dict]:
- if self._keepalive_expiry is not None:
- self._keepalive_sweep()
-
- if (
- self.proxy_mode == "DEFAULT" and url[0] == b"http"
- ) or self.proxy_mode == "FORWARD_ONLY":
- # By default HTTP requests should be forwarded.
- logger.trace(
- "forward_request proxy_origin=%r proxy_headers=%r method=%r url=%r",
- self.proxy_origin,
- self.proxy_headers,
- method,
- url,
- )
- return self._forward_request(
- method, url, headers=headers, stream=stream, extensions=extensions
- )
- else:
- # By default HTTPS should be tunnelled.
- logger.trace(
- "tunnel_request proxy_origin=%r proxy_headers=%r method=%r url=%r",
- self.proxy_origin,
- self.proxy_headers,
- method,
- url,
- )
- return self._tunnel_request(
- method, url, headers=headers, stream=stream, extensions=extensions
- )
-
- def _forward_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: SyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, SyncByteStream, dict]:
- """
- Forwarded proxy requests include the entire URL as the HTTP target,
- rather than just the path.
- """
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
- origin = self.proxy_origin
- connection = self._get_connection_from_pool(origin)
-
- if connection is None:
- connection = SyncHTTPConnection(
- origin=origin,
- http2=self._http2,
- keepalive_expiry=self._keepalive_expiry,
- ssl_context=self._ssl_context,
- )
- self._add_to_pool(connection, timeout)
-
- # Issue a forwarded proxy request...
-
- # GET https://www.example.org/path HTTP/1.1
- # [proxy headers]
- # [headers]
- scheme, host, port, path = url
- if port is None:
- target = b"%b://%b%b" % (scheme, host, path)
- else:
- target = b"%b://%b:%d%b" % (scheme, host, port, path)
-
- url = self.proxy_origin + (target,)
- headers = merge_headers(self.proxy_headers, headers)
-
- (
- status_code,
- headers,
- stream,
- extensions,
- ) = connection.handle_request(
- method, url, headers=headers, stream=stream, extensions=extensions
- )
-
- wrapped_stream = ResponseByteStream(
- stream, connection=connection, callback=self._response_closed
- )
-
- return status_code, headers, wrapped_stream, extensions
-
- def _tunnel_request(
- self,
- method: bytes,
- url: URL,
- headers: Headers,
- stream: SyncByteStream,
- extensions: dict,
- ) -> Tuple[int, Headers, SyncByteStream, dict]:
- """
- Tunnelled proxy requests require an initial CONNECT request to
- establish the connection, and then send regular requests.
- """
- timeout = cast(TimeoutDict, extensions.get("timeout", {}))
- origin = url_to_origin(url)
- connection = self._get_connection_from_pool(origin)
-
- if connection is None:
- scheme, host, port = origin
-
- # First, create a connection to the proxy server
- proxy_connection = SyncHTTPConnection(
- origin=self.proxy_origin,
- http2=self._http2,
- keepalive_expiry=self._keepalive_expiry,
- ssl_context=self._ssl_context,
- )
-
- # Issue a CONNECT request...
-
- # CONNECT www.example.org:80 HTTP/1.1
- # [proxy-headers]
- target = b"%b:%d" % (host, port)
- connect_url = self.proxy_origin + (target,)
- connect_headers = [(b"Host", target), (b"Accept", b"*/*")]
- connect_headers = merge_headers(connect_headers, self.proxy_headers)
-
- try:
- (
- proxy_status_code,
- _,
- proxy_stream,
- _,
- ) = proxy_connection.handle_request(
- b"CONNECT",
- connect_url,
- headers=connect_headers,
- stream=ByteStream(b""),
- extensions=extensions,
- )
-
- proxy_reason = get_reason_phrase(proxy_status_code)
- logger.trace(
- "tunnel_response proxy_status_code=%r proxy_reason=%r ",
- proxy_status_code,
- proxy_reason,
- )
- # Read the response data without closing the socket
- for _ in proxy_stream:
- pass
-
- # See if the tunnel was successfully established.
- if proxy_status_code < 200 or proxy_status_code > 299:
- msg = "%d %s" % (proxy_status_code, proxy_reason)
- raise ProxyError(msg)
-
- # Upgrade to TLS if required
- # We assume the target speaks TLS on the specified port
- if scheme == b"https":
- proxy_connection.start_tls(host, self._ssl_context, timeout)
- except Exception as exc:
- proxy_connection.close()
- raise ProxyError(exc)
-
- # The CONNECT request is successful, so we have now SWITCHED PROTOCOLS.
- # This means the proxy connection is now unusable, and we must create
- # a new one for regular requests, making sure to use the same socket to
- # retain the tunnel.
- connection = SyncHTTPConnection(
- origin=origin,
- http2=self._http2,
- keepalive_expiry=self._keepalive_expiry,
- ssl_context=self._ssl_context,
- socket=proxy_connection.socket,
- )
- self._add_to_pool(connection, timeout)
-
- # Once the connection has been established we can send requests on
- # it as normal.
- (
- status_code,
- headers,
- stream,
- extensions,
- ) = connection.handle_request(
- method,
- url,
- headers=headers,
- stream=stream,
- extensions=extensions,
- )
-
- wrapped_stream = ResponseByteStream(
- stream, connection=connection, callback=self._response_closed
- )
-
- return status_code, headers, wrapped_stream, extensions
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_threadlock.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_threadlock.py
deleted file mode 100644
index 2ff2bc37..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_threadlock.py
+++ /dev/null
@@ -1,35 +0,0 @@
-import threading
-from types import TracebackType
-from typing import Type
-
-
-class ThreadLock:
- """
- Provides thread safety when used as a sync context manager, or a
- no-op when used as an async context manager.
- """
-
- def __init__(self) -> None:
- self.lock = threading.Lock()
-
- def __enter__(self) -> None:
- self.lock.acquire()
-
- def __exit__(
- self,
- exc_type: Type[BaseException] = None,
- exc_value: BaseException = None,
- traceback: TracebackType = None,
- ) -> None:
- self.lock.release()
-
- async def __aenter__(self) -> None:
- pass
-
- async def __aexit__(
- self,
- exc_type: Type[BaseException] = None,
- exc_value: BaseException = None,
- traceback: TracebackType = None,
- ) -> None:
- pass
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_types.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_types.py
deleted file mode 100644
index 2f9eeba7..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_types.py
+++ /dev/null
@@ -1,12 +0,0 @@
-"""
-Type definitions for type checking purposes.
-"""
-
-from typing import List, Mapping, Optional, Tuple, TypeVar, Union
-
-T = TypeVar("T")
-StrOrBytes = Union[str, bytes]
-Origin = Tuple[bytes, bytes, int]
-URL = Tuple[bytes, bytes, Optional[int], bytes]
-Headers = List[Tuple[bytes, bytes]]
-TimeoutDict = Mapping[str, Optional[float]]
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_utils.py b/IKEA_scraper/.venv/Lib/site-packages/httpcore/_utils.py
deleted file mode 100644
index 978b87a2..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpcore/_utils.py
+++ /dev/null
@@ -1,105 +0,0 @@
-import itertools
-import logging
-import os
-import select
-import socket
-import sys
-import typing
-
-from ._types import URL, Origin
-
-_LOGGER_INITIALIZED = False
-TRACE_LOG_LEVEL = 5
-DEFAULT_PORTS = {b"http": 80, b"https": 443}
-
-
-class Logger(logging.Logger):
- # Stub for type checkers.
- def trace(self, message: str, *args: typing.Any, **kwargs: typing.Any) -> None:
- ... # pragma: nocover
-
-
-def get_logger(name: str) -> Logger:
- """
- Get a `logging.Logger` instance, and optionally
- set up debug logging based on the HTTPCORE_LOG_LEVEL or HTTPX_LOG_LEVEL
- environment variables.
- """
- global _LOGGER_INITIALIZED
- if not _LOGGER_INITIALIZED:
- _LOGGER_INITIALIZED = True
- logging.addLevelName(TRACE_LOG_LEVEL, "TRACE")
-
- log_level = os.environ.get(
- "HTTPCORE_LOG_LEVEL", os.environ.get("HTTPX_LOG_LEVEL", "")
- ).upper()
- if log_level in ("DEBUG", "TRACE"):
- logger = logging.getLogger("httpcore")
- logger.setLevel(logging.DEBUG if log_level == "DEBUG" else TRACE_LOG_LEVEL)
- handler = logging.StreamHandler(sys.stderr)
- handler.setFormatter(
- logging.Formatter(
- fmt="%(levelname)s [%(asctime)s] %(name)s - %(message)s",
- datefmt="%Y-%m-%d %H:%M:%S",
- )
- )
- logger.addHandler(handler)
-
- logger = logging.getLogger(name)
-
- def trace(message: str, *args: typing.Any, **kwargs: typing.Any) -> None:
- logger.log(TRACE_LOG_LEVEL, message, *args, **kwargs)
-
- logger.trace = trace # type: ignore
-
- return typing.cast(Logger, logger)
-
-
-def url_to_origin(url: URL) -> Origin:
- scheme, host, explicit_port = url[:3]
- default_port = DEFAULT_PORTS[scheme]
- port = default_port if explicit_port is None else explicit_port
- return scheme, host, port
-
-
-def origin_to_url_string(origin: Origin) -> str:
- scheme, host, explicit_port = origin
- port = f":{explicit_port}" if explicit_port != DEFAULT_PORTS[scheme] else ""
- return f"{scheme.decode('ascii')}://{host.decode('ascii')}{port}"
-
-
-def exponential_backoff(factor: float) -> typing.Iterator[float]:
- yield 0
- for n in itertools.count(2):
- yield factor * (2 ** (n - 2))
-
-
-def is_socket_readable(sock: typing.Optional[socket.socket]) -> bool:
- """
- Return whether a socket, as identifed by its file descriptor, is readable.
-
- "A socket is readable" means that the read buffer isn't empty, i.e. that calling
- .recv() on it would immediately return some data.
- """
- # NOTE: we want check for readability without actually attempting to read, because
- # we don't want to block forever if it's not readable.
-
- # In the case that the socket no longer exists, or cannot return a file
- # descriptor, we treat it as being readable, as if it the next read operation
- # on it is ready to return the terminating `b""`.
- sock_fd = None if sock is None else sock.fileno()
- if sock_fd is None or sock_fd < 0:
- return True
-
- # The implementation below was stolen from:
- # https://github.com/python-trio/trio/blob/20ee2b1b7376db637435d80e266212a35837ddcc/trio/_socket.py#L471-L478
- # See also: https://github.com/encode/httpcore/pull/193#issuecomment-703129316
-
- # Use select.select on Windows, and when poll is unavailable and select.poll
- # everywhere else. (E.g. When eventlet is in use. See #327)
- if sys.platform == "win32" or getattr(select, "poll", None) is None:
- rready, _, _ = select.select([sock_fd], [], [], 0)
- return bool(rready)
- p = select.poll()
- p.register(sock_fd, select.POLLIN)
- return bool(p.poll(0))
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpcore/py.typed b/IKEA_scraper/.venv/Lib/site-packages/httpcore/py.typed
deleted file mode 100644
index e69de29b..00000000
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/INSTALLER b/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/INSTALLER
deleted file mode 100644
index a1b589e3..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/LICENSE.md b/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/LICENSE.md
deleted file mode 100644
index ab79d16a..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/LICENSE.md
+++ /dev/null
@@ -1,12 +0,0 @@
-Copyright © 2019, [Encode OSS Ltd](https://www.encode.io/).
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-* Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/METADATA b/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/METADATA
deleted file mode 100644
index 8a07aad3..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/METADATA
+++ /dev/null
@@ -1,990 +0,0 @@
-Metadata-Version: 2.1
-Name: httpx
-Version: 0.19.0
-Summary: The next generation HTTP client.
-Home-page: https://github.com/encode/httpx
-Author: Tom Christie
-Author-email: tom@tomchristie.com
-License: BSD
-Project-URL: Changelog, https://github.com/encode/httpx/blob/master/CHANGELOG.md
-Project-URL: Documentation, https://www.python-httpx.org
-Project-URL: Source, https://github.com/encode/httpx
-Platform: UNKNOWN
-Classifier: Development Status :: 4 - Beta
-Classifier: Environment :: Web Environment
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: BSD License
-Classifier: Operating System :: OS Independent
-Classifier: Topic :: Internet :: WWW/HTTP
-Classifier: Framework :: AsyncIO
-Classifier: Framework :: Trio
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: 3.10
-Classifier: Programming Language :: Python :: 3 :: Only
-Requires-Python: >=3.6
-Description-Content-Type: text/markdown
-Requires-Dist: certifi
-Requires-Dist: charset-normalizer
-Requires-Dist: sniffio
-Requires-Dist: rfc3986[idna2008] (<2,>=1.3)
-Requires-Dist: httpcore (<0.14.0,>=0.13.3)
-Requires-Dist: async-generator ; python_version < "3.7"
-Provides-Extra: brotli
-Requires-Dist: brotlicffi ; (platform_python_implementation != "CPython") and extra == 'brotli'
-Requires-Dist: brotli ; (platform_python_implementation == "CPython") and extra == 'brotli'
-Provides-Extra: http2
-Requires-Dist: h2 (<5,>=3) ; extra == 'http2'
-
-
-
-
-
-HTTPX - A next-generation HTTP client for Python.
-
-
-
-
-
-
-
-
-
-
-HTTPX is a fully featured HTTP client for Python 3, which provides sync and async APIs, and support for both HTTP/1.1 and HTTP/2.
-
-**Note**: _HTTPX should be considered in beta. We believe we've got the public API to
-a stable point now, but would strongly recommend pinning your dependencies to the `0.19.*`
-release, so that you're able to properly review [API changes between package updates](https://github.com/encode/httpx/blob/master/CHANGELOG.md). A 1.0 release is expected to be issued sometime in 2021._
-
----
-
-Let's get started...
-
-```pycon
->>> import httpx
->>> r = httpx.get('https://www.example.org/')
->>> r
-
->>> r.status_code
-200
->>> r.headers['content-type']
-'text/html; charset=UTF-8'
->>> r.text
-'\n\n\nExample Domain...'
-```
-
-Or, using the async API...
-
-_Use [IPython](https://ipython.readthedocs.io/en/stable/) or Python 3.8+ with `python -m asyncio` to try this code interactively._
-
-```pycon
->>> import httpx
->>> async with httpx.AsyncClient() as client:
-... r = await client.get('https://www.example.org/')
-...
->>> r
-
-```
-
-## Features
-
-HTTPX builds on the well-established usability of `requests`, and gives you:
-
-* A broadly [requests-compatible API](https://www.python-httpx.org/compatibility/).
-* Standard synchronous interface, but with [async support if you need it](https://www.python-httpx.org/async/).
-* HTTP/1.1 [and HTTP/2 support](https://www.python-httpx.org/http2/).
-* Ability to make requests directly to [WSGI applications](https://www.python-httpx.org/advanced/#calling-into-python-web-apps) or [ASGI applications](https://www.python-httpx.org/async/#calling-into-python-web-apps).
-* Strict timeouts everywhere.
-* Fully type annotated.
-* 100% test coverage.
-
-Plus all the standard features of `requests`...
-
-* International Domains and URLs
-* Keep-Alive & Connection Pooling
-* Sessions with Cookie Persistence
-* Browser-style SSL Verification
-* Basic/Digest Authentication
-* Elegant Key/Value Cookies
-* Automatic Decompression
-* Automatic Content Decoding
-* Unicode Response Bodies
-* Multipart File Uploads
-* HTTP(S) Proxy Support
-* Connection Timeouts
-* Streaming Downloads
-* .netrc Support
-* Chunked Requests
-
-## Installation
-
-Install with pip:
-
-```shell
-$ pip install httpx
-```
-
-Or, to include the optional HTTP/2 support, use:
-
-```shell
-$ pip install httpx[http2]
-```
-
-HTTPX requires Python 3.6+.
-
-## Documentation
-
-Project documentation is available at [https://www.python-httpx.org/](https://www.python-httpx.org/).
-
-For a run-through of all the basics, head over to the [QuickStart](https://www.python-httpx.org/quickstart/).
-
-For more advanced topics, see the [Advanced Usage](https://www.python-httpx.org/advanced/) section, the [async support](https://www.python-httpx.org/async/) section, or the [HTTP/2](https://www.python-httpx.org/http2/) section.
-
-The [Developer Interface](https://www.python-httpx.org/api/) provides a comprehensive API reference.
-
-To find out about tools that integrate with HTTPX, see [Third Party Packages](https://www.python-httpx.org/third_party_packages/).
-
-## Contribute
-
-If you want to contribute with HTTPX check out the [Contributing Guide](https://www.python-httpx.org/contributing/) to learn how to start.
-
-## Dependencies
-
-The HTTPX project relies on these excellent libraries:
-
-* `httpcore` - The underlying transport implementation for `httpx`.
- * `h11` - HTTP/1.1 support.
- * `h2` - HTTP/2 support. *(Optional)*
-* `certifi` - SSL certificates.
-* `charset_normalizer` - Charset auto-detection.
-* `rfc3986` - URL parsing & normalization.
- * `idna` - Internationalized domain name support.
-* `sniffio` - Async library autodetection.
-* `async_generator` - Backport support for `contextlib.asynccontextmanager`. *(Only required for Python 3.6)*
-* `brotli` or `brotlicffi` - Decoding for "brotli" compressed responses. *(Optional)*
-
-A huge amount of credit is due to `requests` for the API layout that
-much of this work follows, as well as to `urllib3` for plenty of design
-inspiration around the lower-level networking details.
-
-— ⭐️ —
-HTTPX is BSD licensed code. Designed & built in Brighton, England.
-
-
-# Changelog
-
-All notable changes to this project will be documented in this file.
-
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
-
-## 0.19.0 (19th June, 2021)
-
-### Added
-
-* Add support for `Client(allow_redirects=)`. (Pull #1790)
-* Add automatic character set detection, when no `charset` is included in the response `Content-Type` header. (Pull #1791)
-
-### Changed
-
-* Event hooks are now also called for any additional redirect or auth requests/responses. (Pull #1806)
-* Strictly enforce that upload files must be opened in binary mode. (Pull #1736)
-* Strictly enforce that client instances can only be opened and closed once, and cannot be re-opened. (Pull #1800)
-* Drop `mode` argument from `httpx.Proxy(..., mode=...)`. (Pull #1795)
-
-## 0.18.2 (17th June, 2021)
-
-### Added
-
-* Support for Python 3.10. (Pull #1687)
-* Expose `httpx.USE_CLIENT_DEFAULT`, used as the default to `auth` and `timeout` parameters in request methods. (Pull #1634)
-* Support [HTTP/2 "prior knowledge"](https://python-hyper.org/projects/hyper-h2/en/v2.3.1/negotiating-http2.html#prior-knowledge), using `httpx.Client(http1=False, http2=True)`. (Pull #1624)
-
-### Fixed
-
-* Clean up some cases where warnings were being issued. (Pull #1687)
-* Prefer Content-Length over Transfer-Encoding: chunked for content= cases. (Pull #1619)
-
-## 0.18.1 (29th April, 2021)
-
-### Changed
-
-* Update brotli support to use the `brotlicffi` package (Pull #1605)
-* Ensure that `Request(..., stream=...)` does not auto-generate any headers on the request instance. (Pull #1607)
-
-### Fixed
-
-* Pass through `timeout=...` in top-level httpx.stream() function. (Pull #1613)
-* Map httpcore transport close exceptions to httpx exceptions. (Pull #1606)
-
-## 0.18.0 (27th April, 2021)
-
-The 0.18.x release series formalises our low-level Transport API, introducing the base classes `httpx.BaseTransport` and `httpx.AsyncBaseTransport`.
-
-See the "[Writing custom transports](https://www.python-httpx.org/advanced/#writing-custom-transports)" documentation and the [`httpx.BaseTransport.handle_request()`](https://github.com/encode/httpx/blob/397aad98fdc8b7580a5fc3e88f1578b4302c6382/httpx/_transports/base.py#L77-L147) docstring for more complete details on implementing custom transports.
-
-Pull request #1522 includes a checklist of differences from the previous `httpcore` transport API, for developers implementing custom transports.
-
-The following API changes have been issuing deprecation warnings since 0.17.0 onwards, and are now fully deprecated...
-
-* You should now use httpx.codes consistently instead of httpx.StatusCodes.
-* Use limits=... instead of pool_limits=....
-* Use proxies={"http://": ...} instead of proxies={"http": ...} for scheme-specific mounting.
-
-### Changed
-
-* Transport instances now inherit from `httpx.BaseTransport` or `httpx.AsyncBaseTransport`,
- and should implement either the `handle_request` method or `handle_async_request` method. (Pull #1522, #1550)
-* The `response.ext` property and `Response(ext=...)` argument are now named `extensions`. (Pull #1522)
-* The recommendation to not use `data=` in favour of `content=` has now been escalated to a deprecation warning. (Pull #1573)
-* Drop `Response(on_close=...)` from API, since it was a bit of leaking implementation detail. (Pull #1572)
-* When using a client instance, cookies should always be set on the client, rather than on a per-request basis. We prefer enforcing a stricter API here because it provides clearer expectations around cookie persistence, particularly when redirects occur. (Pull #1574)
-* The runtime exception `httpx.ResponseClosed` is now named `httpx.StreamClosed`. (#1584)
-* The `httpx.QueryParams` model now presents an immutable interface. There is a discussion on [the design and motivation here](https://github.com/encode/httpx/discussions/1599). Use `client.params = client.params.merge(...)` instead of `client.params.update(...)`. The basic query manipulation methods are `query.set(...)`, `query.add(...)`, and `query.remove()`. (#1600)
-
-### Added
-
-* The `Request` and `Response` classes can now be serialized using pickle. (#1579)
-* Handle `data={"key": [None|int|float|bool]}` cases. (Pull #1539)
-* Support `httpx.URL(**kwargs)`, for example `httpx.URL(scheme="https", host="www.example.com", path="/')`, or `httpx.URL("https://www.example.com/", username="tom@gmail.com", password="123 456")`. (Pull #1601)
-* Support `url.copy_with(params=...)`. (Pull #1601)
-* Add `url.params` parameter, returning an immutable `QueryParams` instance. (Pull #1601)
-* Support query manipulation methods on the URL class. These are `url.copy_set_param()`, `url.copy_add_param()`, `url.copy_remove_param()`, `url.copy_merge_params()`. (Pull #1601)
-* The `httpx.URL` class now performs port normalization, so `:80` ports are stripped from `http` URLs and `:443` ports are stripped from `https` URLs. (Pull #1603)
-* The `URL.host` property returns unicode strings for internationalized domain names. The `URL.raw_host` property returns byte strings with IDNA escaping applied. (Pull #1590)
-
-### Fixed
-
-* Fix Content-Length for cases of `files=...` where unicode string is used as the file content. (Pull #1537)
-* Fix some cases of merging relative URLs against `Client(base_url=...)`. (Pull #1532)
-* The `request.content` attribute is now always available except for streaming content, which requires an explicit `.read()`. (Pull #1583)
-
-## 0.17.1 (March 15th, 2021)
-
-### Fixed
-
-* Type annotation on `CertTypes` allows `keyfile` and `password` to be optional. (Pull #1503)
-* Fix httpcore pinned version. (Pull #1495)
-
-## 0.17.0 (February 28th, 2021)
-
-### Added
-
-* Add `httpx.MockTransport()`, allowing to mock out a transport using pre-determined responses. (Pull #1401, Pull #1449)
-* Add `httpx.HTTPTransport()` and `httpx.AsyncHTTPTransport()` default transports. (Pull #1399)
-* Add mount API support, using `httpx.Client(mounts=...)`. (Pull #1362)
-* Add `chunk_size` parameter to `iter_raw()`, `iter_bytes()`, `iter_text()`. (Pull #1277)
-* Add `keepalive_expiry` parameter to `httpx.Limits()` configuration. (Pull #1398)
-* Add repr to `httpx.Cookies` to display available cookies. (Pull #1411)
-* Add support for `params=` (previously only `params=` was supported). (Pull #1426)
-
-### Fixed
-
-* Add missing `raw_path` to ASGI scope. (Pull #1357)
-* Tweak `create_ssl_context` defaults to use `trust_env=True`. (Pull #1447)
-* Properly URL-escape WSGI `PATH_INFO`. (Pull #1391)
-* Properly set default ports in WSGI transport. (Pull #1469)
-* Properly encode slashes when using `base_url`. (Pull #1407)
-* Properly map exceptions in `request.aclose()`. (Pull #1465)
-
-## 0.16.1 (October 8th, 2020)
-
-### Fixed
-
-* Support literal IPv6 addresses in URLs. (Pull #1349)
-* Force lowercase headers in ASGI scope dictionaries. (Pull #1351)
-
-## 0.16.0 (October 6th, 2020)
-
-### Changed
-
-* Preserve HTTP header casing. (Pull #1338, encode/httpcore#216, python-hyper/h11#104)
-* Drop `response.next()` and `response.anext()` methods in favour of `response.next_request` attribute. (Pull #1339)
-* Closed clients now raise a runtime error if attempting to send a request. (Pull #1346)
-
-### Added
-
-* Add Python 3.9 to officially supported versions.
-* Type annotate `__enter__`/`__exit__`/`__aenter__`/`__aexit__` in a way that supports subclasses of `Client` and `AsyncClient`. (Pull #1336)
-
-## 0.15.5 (October 1st, 2020)
-
-### Added
-
-* Add `response.next_request` (Pull #1334)
-
-## 0.15.4 (September 25th, 2020)
-
-### Added
-
-* Support direct comparisons between `Headers` and dicts or lists of two-tuples. Eg. `assert response.headers == {"Content-Length": 24}` (Pull #1326)
-
-### Fixed
-
-* Fix automatic `.read()` when `Response` instances are created with `content=` (Pull #1324)
-
-## 0.15.3 (September 24th, 2020)
-
-### Fixed
-
-* Fixed connection leak in async client due to improper closing of response streams. (Pull #1316)
-
-## 0.15.2 (September 23nd, 2020)
-
-### Fixed
-
-* Fixed `response.elapsed` property. (Pull #1313)
-* Fixed client authentication interaction with `.stream()`. (Pull #1312)
-
-## 0.15.1 (September 23nd, 2020)
-
-### Fixed
-
-* ASGITransport now properly applies URL decoding to the `path` component, as-per the ASGI spec. (Pull #1307)
-
-## 0.15.0 (September 22nd, 2020)
-
-### Added
-
-* Added support for curio. (Pull https://github.com/encode/httpcore/pull/168)
-* Added support for event hooks. (Pull #1246)
-* Added support for authentication flows which require either sync or async I/O. (Pull #1217)
-* Added support for monitoring download progress with `response.num_bytes_downloaded`. (Pull #1268)
-* Added `Request(content=...)` for byte content, instead of overloading `Request(data=...)` (Pull #1266)
-* Added support for all URL components as parameter names when using `url.copy_with(...)`. (Pull #1285)
-* Neater split between automatically populated headers on `Request` instances, vs default `client.headers`. (Pull #1248)
-* Unclosed `AsyncClient` instances will now raise warnings if garbage collected. (Pull #1197)
-* Support `Response(content=..., text=..., html=..., json=...)` for creating usable response instances in code. (Pull #1265, #1297)
-* Support instantiating requests from the low-level transport API. (Pull #1293)
-* Raise errors on invalid URL types. (Pull #1259)
-
-### Changed
-
-* Cleaned up expected behaviour for URL escaping. `url.path` is now URL escaped. (Pull #1285)
-* Cleaned up expected behaviour for bytes vs str in URL components. `url.userinfo` and `url.query` are not URL escaped, and so return bytes. (Pull #1285)
-* Drop `url.authority` property in favour of `url.netloc`, since "authority" was semantically incorrect. (Pull #1285)
-* Drop `url.full_path` property in favour of `url.raw_path`, for better consistency with other parts of the API. (Pull #1285)
-* No longer use the `chardet` library for auto-detecting charsets, instead defaulting to a simpler approach when no charset is specified. (#1269)
-
-### Fixed
-
-* Swapped ordering of redirects and authentication flow. (Pull #1267)
-* `.netrc` lookups should use host, not host+port. (Pull #1298)
-
-### Removed
-
-* The `URLLib3Transport` class no longer exists. We've published it instead as an example of [a custom transport class](https://gist.github.com/florimondmanca/d56764d78d748eb9f73165da388e546e). (Pull #1182)
-* Drop `request.timer` attribute, which was being used internally to set `response.elapsed`. (Pull #1249)
-* Drop `response.decoder` attribute, which was being used internally. (Pull #1276)
-* `Request.prepare()` is now a private method. (Pull #1284)
-* The `Headers.getlist()` method had previously been deprecated in favour of `Headers.get_list()`. It is now fully removed.
-* The `QueryParams.getlist()` method had previously been deprecated in favour of `QueryParams.get_list()`. It is now fully removed.
-* The `URL.is_ssl` property had previously been deprecated in favour of `URL.scheme == "https"`. It is now fully removed.
-* The `httpx.PoolLimits` class had previously been deprecated in favour of `httpx.Limits`. It is now fully removed.
-* The `max_keepalive` setting had previously been deprecated in favour of the more explicit `max_keepalive_connections`. It is now fully removed.
-* The verbose `httpx.Timeout(5.0, connect_timeout=60.0)` style had previously been deprecated in favour of `httpx.Timeout(5.0, connect=60.0)`. It is now fully removed.
-* Support for instantiating a timeout config missing some defaults, such as `httpx.Timeout(connect=60.0)`, had previously been deprecated in favour of enforcing a more explicit style, such as `httpx.Timeout(5.0, connect=60.0)`. This is now strictly enforced.
-
-## 0.14.3 (September 2nd, 2020)
-
-### Added
-
-* `http.Response()` may now be instantiated without a `request=...` parameter. Useful for some unit testing cases. (Pull #1238)
-* Add `103 Early Hints` and `425 Too Early` status codes. (Pull #1244)
-
-### Fixed
-
-* `DigestAuth` now handles responses that include multiple 'WWW-Authenticate' headers. (Pull #1240)
-* Call into transport `__enter__`/`__exit__` or `__aenter__`/`__aexit__` when client is used in a context manager style. (Pull #1218)
-
-## 0.14.2 (August 24th, 2020)
-
-### Added
-
-* Support `client.get(..., auth=None)` to bypass the default authentication on a clients. (Pull #1115)
-* Support `client.auth = ...` property setter. (Pull #1185)
-* Support `httpx.get(..., proxies=...)` on top-level request functions. (Pull #1198)
-* Display instances with nicer import styles. (Eg. ) (Pull #1155)
-* Support `cookies=[(key, value)]` list-of-two-tuples style usage. (Pull #1211)
-
-### Fixed
-
-* Ensure that automatically included headers on a request may be modified. (Pull #1205)
-* Allow explicit `Content-Length` header on streaming requests. (Pull #1170)
-* Handle URL quoted usernames and passwords properly. (Pull #1159)
-* Use more consistent default for `HEAD` requests, setting `allow_redirects=True`. (Pull #1183)
-* If a transport error occurs while streaming the response, raise an `httpx` exception, not the underlying `httpcore` exception. (Pull #1190)
-* Include the underlying `httpcore` traceback, when transport exceptions occur. (Pull #1199)
-
-## 0.14.1 (August 11th, 2020)
-
-### Added
-
-* The `httpx.URL(...)` class now raises `httpx.InvalidURL` on invalid URLs, rather than exposing the underlying `rfc3986` exception. If a redirect response includes an invalid 'Location' header, then a `RemoteProtocolError` exception is raised, which will be associated with the request that caused it. (Pull #1163)
-
-### Fixed
-
-* Handling multiple `Set-Cookie` headers became broken in the 0.14.0 release, and is now resolved. (Pull #1156)
-
-## 0.14.0 (August 7th, 2020)
-
-The 0.14 release includes a range of improvements to the public API, intended on preparing for our upcoming 1.0 release.
-
-* Our HTTP/2 support is now fully optional. **You now need to use `pip install httpx[http2]` if you want to include the HTTP/2 dependancies.**
-* Our HSTS support has now been removed. Rewriting URLs from `http` to `https` if the host is on the HSTS list can be beneficial in avoiding roundtrips to incorrectly formed URLs, but on balance we've decided to remove this feature, on the principle of least surprise. Most programmatic clients do not include HSTS support, and for now we're opting to remove our support for it.
-* Our exception hierarchy has been overhauled. Most users will want to stick with their existing `httpx.HTTPError` usage, but we've got a clearer overall structure now. See https://www.python-httpx.org/exceptions/ for more details.
-
-When upgrading you should be aware of the following public API changes. Note that deprecated usages will currently continue to function, but will issue warnings.
-
-* You should now use `httpx.codes` consistently instead of `httpx.StatusCodes`.
-* Usage of `httpx.Timeout()` should now always include an explicit default. Eg. `httpx.Timeout(None, pool=5.0)`.
-* When using `httpx.Timeout()`, we now have more concisely named keyword arguments. Eg. `read=5.0`, instead of `read_timeout=5.0`.
-* Use `httpx.Limits()` instead of `httpx.PoolLimits()`, and `limits=...` instead of `pool_limits=...`.
-* The `httpx.Limits(max_keepalive=...)` argument is now deprecated in favour of a more explicit `httpx.Limits(max_keepalive_connections=...)`.
-* Keys used with `Client(proxies={...})` should now be in the style of `{"http://": ...}`, rather than `{"http": ...}`.
-* The multidict methods `Headers.getlist()` and `QueryParams.getlist()` are deprecated in favour of more consistent `.get_list()` variants.
-* The `URL.is_ssl` property is deprecated in favour of `URL.scheme == "https"`.
-* The `URL.join(relative_url=...)` method is now `URL.join(url=...)`. This change does not support warnings for the deprecated usage style.
-
-One notable aspect of the 0.14.0 release is that it tightens up the public API for `httpx`, by ensuring that several internal attributes and methods have now become strictly private.
-
-The following previously had nominally public names on the client, but were all undocumented and intended solely for internal usage. They are all now replaced with underscored names, and should not be relied on or accessed.
-
-These changes should not affect users who have been working from the `httpx` documentation.
-
-* `.merge_url()`, `.merge_headers()`, `.merge_cookies()`, `.merge_queryparams()`
-* `.build_auth()`, `.build_redirect_request()`
-* `.redirect_method()`, `.redirect_url()`, `.redirect_headers()`, `.redirect_stream()`
-* `.send_handling_redirects()`, `.send_handling_auth()`, `.send_single_request()`
-* `.init_transport()`, `.init_proxy_transport()`
-* `.proxies`, `.transport`, `.netrc`, `.get_proxy_map()`
-
-See pull requests #997, #1065, #1071.
-
-Some areas of API which were already on the deprecation path, and were raising warnings or errors in 0.13.x have now been escalated to being fully removed.
-
-* Drop `ASGIDispatch`, `WSGIDispatch`, which have been replaced by `ASGITransport`, `WSGITransport`.
-* Drop `dispatch=...`` on client, which has been replaced by `transport=...``
-* Drop `soft_limit`, `hard_limit`, which have been replaced by `max_keepalive` and `max_connections`.
-* Drop `Response.stream` and` `Response.raw`, which have been replaced by ``.aiter_bytes` and `.aiter_raw`.
-* Drop `proxies=` in favor of `proxies=httpx.Proxy(...)`.
-
-See pull requests #1057, #1058.
-
-### Added
-
-* Added dedicated exception class `httpx.HTTPStatusError` for `.raise_for_status()` exceptions. (Pull #1072)
-* Added `httpx.create_ssl_context()` helper function. (Pull #996)
-* Support for proxy exlcusions like `proxies={"https://www.example.com": None}`. (Pull #1099)
-* Support `QueryParams(None)` and `client.params = None`. (Pull #1060)
-
-### Changed
-
-* Use `httpx.codes` consistently in favour of `httpx.StatusCodes` which is placed into deprecation. (Pull #1088)
-* Usage of `httpx.Timeout()` should now always include an explicit default. Eg. `httpx.Timeout(None, pool=5.0)`. (Pull #1085)
-* Switch to more concise `httpx.Timeout()` keyword arguments. Eg. `read=5.0`, instead of `read_timeout=5.0`. (Pull #1111)
-* Use `httpx.Limits()` instead of `httpx.PoolLimits()`, and `limits=...` instead of `pool_limits=...`. (Pull #1113)
-* Keys used with `Client(proxies={...})` should now be in the style of `{"http://": ...}`, rather than `{"http": ...}`. (Pull #1127)
-* The multidict methods `Headers.getlist` and `QueryParams.getlist` are deprecated in favour of more consistent `.get_list()` variants. (Pull #1089)
-* `URL.port` becomes `Optional[int]`. Now only returns a port if one is explicitly included in the URL string. (Pull #1080)
-* The `URL(..., allow_relative=[bool])` parameter no longer exists. All URL instances may be relative. (Pull #1073)
-* Drop unnecessary `url.full_path = ...` property setter. (Pull #1069)
-* The `URL.join(relative_url=...)` method is now `URL.join(url=...)`. (Pull #1129)
-* The `URL.is_ssl` property is deprecated in favour of `URL.scheme == "https"`. (Pull #1128)
-
-### Fixed
-
-* Add missing `Response.next()` method. (Pull #1055)
-* Ensure all exception classes are exposed as public API. (Pull #1045)
-* Support multiple items with an identical field name in multipart encodings. (Pull #777)
-* Skip HSTS preloading on single-label domains. (Pull #1074)
-* Fixes for `Response.iter_lines()`. (Pull #1033, #1075)
-* Ignore permission errors when accessing `.netrc` files. (Pull #1104)
-* Allow bare hostnames in `HTTP_PROXY` etc... environment variables. (Pull #1120)
-* Settings `app=...` or `transport=...` bypasses any environment based proxy defaults. (Pull #1122)
-* Fix handling of `.base_url` when a path component is included in the base URL. (Pull #1130)
-
----
-
-## 0.13.3 (May 29th, 2020)
-
-### Fixed
-
-* Include missing keepalive expiry configuration. (Pull #1005)
-* Improved error message when URL redirect has a custom scheme. (Pull #1002)
-
-## 0.13.2 (May 27th, 2020)
-
-### Fixed
-
-* Include explicit "Content-Length: 0" on POST, PUT, PATCH if no request body is used. (Pull #995)
-* Add `http2` option to `httpx.Client`. (Pull #982)
-* Tighten up API typing in places. (Pull #992, #999)
-
-## 0.13.1 (May 22nd, 2020)
-
-### Fixed
-
-* Fix pool options deprecation warning. (Pull #980)
-* Include `httpx.URLLib3ProxyTransport` in top-level API. (Pull #979)
-
-## 0.13.0 (May 22nd, 2020)
-
-This release switches to `httpcore` for all the internal networking, which means:
-
-* We're using the same codebase for both our sync and async clients.
-* HTTP/2 support is now available with the sync client.
-* We no longer have a `urllib3` dependency for our sync client, although there is still an *optional* `URLLib3Transport` class.
-
-It also means we've had to remove our UDS support, since maintaining that would have meant having to push back our work towards a 1.0 release, which isn't a trade-off we wanted to make.
-
-We also now have [a public "Transport API"](https://www.python-httpx.org/advanced/#custom-transports), which you can use to implement custom transport implementations against. This formalises and replaces our previously private "Dispatch API".
-
-### Changed
-
-* Use `httpcore` for underlying HTTP transport. Drop `urllib3` requirement. (Pull #804, #967)
-* Rename pool limit options from `soft_limit`/`hard_limit` to `max_keepalive`/`max_connections`. (Pull #968)
-* The previous private "Dispatch API" has now been promoted to a public "Transport API". When customizing the transport use `transport=...`. The `ASGIDispatch` and `WSGIDispatch` class naming is deprecated in favour of `ASGITransport` and `WSGITransport`. (Pull #963)
-
-### Added
-
-* Added `URLLib3Transport` class for optional `urllib3` transport support. (Pull #804, #963)
-* Streaming multipart uploads. (Pull #857)
-* Logging via HTTPCORE_LOG_LEVEL and HTTPX_LOG_LEVEL environment variables
-and TRACE level logging. (Pull encode/httpcore#79)
-
-### Fixed
-
-* Performance improvement in brotli decoder. (Pull #906)
-* Proper warning level of deprecation notice in `Response.stream` and `Response.raw`. (Pull #908)
-* Fix support for generator based WSGI apps. (Pull #887)
-* Reuse of connections on HTTP/2 in close concurrency situations. (Pull encode/httpcore#81)
-* Honor HTTP/2 max concurrent streams settings (Pull encode/httpcore#89, encode/httpcore#90)
-* Fix bytes support in multipart uploads. (Pull #974)
-* Improve typing support for `files=...`. (Pull #976)
-
-### Removed
-
-* Dropped support for `Client(uds=...)` (Pull #804)
-
-## 0.13.0.dev2 (May 12th, 2020)
-
-The 0.13.0.dev2 is a *pre-release* version. To install it, use `pip install httpx --pre`.
-
-### Added
-
-* Logging via HTTPCORE_LOG_LEVEL and HTTPX_LOG_LEVEL environment variables
-and TRACE level logging. (HTTPCore Pull #79)
-
-### Fixed
-
-* Reuse of connections on HTTP/2 in close concurrency situations. (HTTPCore Pull #81)
-* When using an `app=` observe neater disconnect behaviour instead of sending empty body messages. (Pull #919)
-
-## 0.13.0.dev1 (May 6th, 2020)
-
-The 0.13.0.dev1 is a *pre-release* version. To install it, use `pip install httpx --pre`.
-
-### Fixed
-
-* Passing `http2` flag to proxy dispatchers. (Pull #934)
-* Use [`httpcore` v0.8.3](https://github.com/encode/httpcore/releases/tag/0.8.3)
-which addresses problems in handling of headers when using proxies.
-
-## 0.13.0.dev0 (April 30th, 2020)
-
-The 0.13.0.dev0 is a *pre-release* version. To install it, use `pip install httpx --pre`.
-
-This release switches to `httpcore` for all the internal networking, which means:
-
-* We're using the same codebase for both our sync and async clients.
-* HTTP/2 support is now available with the sync client.
-* We no longer have a `urllib3` dependency for our sync client, although there is still an *optional* `URLLib3Dispatcher` class.
-
-It also means we've had to remove our UDS support, since maintaining that would have meant having to push back our work towards a 1.0 release, which isn't a trade-off we wanted to make.
-
-### Changed
-
-* Use `httpcore` for underlying HTTP transport. Drop `urllib3` requirement. (Pull #804)
-
-### Added
-
-* Added `URLLib3Dispatcher` class for optional `urllib3` transport support. (Pull #804)
-* Streaming multipart uploads. (Pull #857)
-
-### Fixed
-
-* Performance improvement in brotli decoder. (Pull #906)
-* Proper warning level of deprecation notice in `Response.stream` and `Response.raw`. (Pull #908)
-* Fix support for generator based WSGI apps. (Pull #887)
-
-### Removed
-
-* Dropped support for `Client(uds=...)` (Pull #804)
-
----
-
-## 0.12.1 (March 19th, 2020)
-
-### Fixed
-
-* Resolved packaging issue, where additional files were being included.
-
-## 0.12.0 (March 9th, 2020)
-
-The 0.12 release tightens up the API expectations for `httpx` by switching to private module names to enforce better clarity around public API.
-
-All imports of `httpx` should import from the top-level package only, such as `from httpx import Request`, rather than importing from privately namespaced modules such as `from httpx._models import Request`.
-
-### Added
-
-* Support making response body available to auth classes with `.requires_response_body`. (Pull #803)
-* Export `NetworkError` exception. (Pull #814)
-* Add support for `NO_PROXY` environment variable. (Pull #835)
-
-### Changed
-
-* Switched to private module names. (Pull #785)
-* Drop redirect looping detection and the `RedirectLoop` exception, instead using `TooManyRedirects`. (Pull #819)
-* Drop `backend=...` parameter on `AsyncClient`, in favour of always autodetecting `trio`/`asyncio`. (Pull #791)
-
-### Fixed
-
-* Support basic auth credentials in proxy URLs. (Pull #780)
-* Fix `httpx.Proxy(url, mode="FORWARD_ONLY")` configuration. (Pull #788)
-* Fallback to setting headers as UTF-8 if no encoding is specified. (Pull #820)
-* Close proxy dispatches classes on client close. (Pull #826)
-* Support custom `cert` parameters even if `verify=False`. (Pull #796)
-* Don't support invalid dict-of-dicts form data in `data=...`. (Pull #811)
-
----
-
-## 0.11.1 (January 17th, 2020)
-
-### Fixed
-
-* Fixed usage of `proxies=...` on `Client()`. (Pull #763)
-* Support both `zlib` and `deflate` style encodings on `Content-Encoding: deflate`. (Pull #758)
-* Fix for streaming a redirect response body with `allow_redirects=False`. (Pull #766)
-* Handle redirect with malformed Location headers missing host. (Pull #774)
-
-## 0.11.0 (January 9th, 2020)
-
-The 0.11 release reintroduces our sync support, so that `httpx` now supports both a standard thread-concurrency API, and an async API.
-
-Existing async `httpx` users that are upgrading to 0.11 should ensure that:
-
-* Async codebases should always use a client instance to make requests, instead of the top-level API.
-* The async client is named as `httpx.AsyncClient()`, instead of `httpx.Client()`.
-* When instantiating proxy configurations use the `httpx.Proxy()` class, instead of the previous `httpx.HTTPProxy()`. This new configuration class works for configuring both sync and async clients.
-
-We believe the API is now pretty much stable, and are aiming for a 1.0 release sometime on or before April 2020.
-
-### Changed
-
-- Top level API such as `httpx.get(url, ...)`, `httpx.post(url, ...)`, `httpx.request(method, url, ...)` becomes synchronous.
-- Added `httpx.Client()` for synchronous clients, with `httpx.AsyncClient` being used for async clients.
-- Switched to `proxies=httpx.Proxy(...)` for proxy configuration.
-- Network connection errors are wrapped in `httpx.NetworkError`, rather than exposing lower-level exception types directly.
-
-### Removed
-
-- The `request.url.origin` property and `httpx.Origin` class are no longer available.
-- The per-request `cert`, `verify`, and `trust_env` arguments are escalated from raising errors if used, to no longer being available. These arguments should be used on a per-client instance instead, or in the top-level API.
-- The `stream` argument has escalated from raising an error when used, to no longer being available. Use the `client.stream(...)` or `httpx.stream()` streaming API instead.
-
-### Fixed
-
-- Redirect loop detection matches against `(method, url)` rather than `url`. (Pull #734)
-
----
-
-## 0.10.1 (December 31st, 2019)
-
-### Fixed
-
-- Fix issue with concurrent connection acquiry. (Pull #700)
-- Fix write error on closing HTTP/2 connections. (Pull #699)
-
-## 0.10.0 (December 29th, 2019)
-
-The 0.10.0 release makes some changes that will allow us to support both sync and async interfaces.
-
-In particular with streaming responses the `response.read()` method becomes `response.aread()`, and the `response.close()` method becomes `response.aclose()`.
-
-If following redirects explicitly the `response.next()` method becomes `response.anext()`.
-
-### Fixed
-
-- End HTTP/2 streams immediately on no-body requests, rather than sending an empty body message. (Pull #682)
-- Improve typing for `Response.request`: switch from `Optional[Request]` to `Request`. (Pull #666)
-- `Response.elapsed` now reflects the entire download time. (Pull #687, #692)
-
-### Changed
-
-- Added `AsyncClient` as a synonym for `Client`. (Pull #680)
-- Switch to `response.aread()` for conditionally reading streaming responses. (Pull #674)
-- Switch to `response.aclose()` and `client.aclose()` for explicit closing. (Pull #674, #675)
-- Switch to `response.anext()` for resolving the next redirect response. (Pull #676)
-
-### Removed
-
-- When using a client instance, the per-request usage of `verify`, `cert`, and `trust_env` have now escalated from raising a warning to raising an error. You should set these arguments on the client instead. (Pull #617)
-- Removed the undocumented `request.read()`, since end users should not require it.
-
----
-
-## 0.9.5 (December 20th, 2019)
-
-### Fixed
-
-- Fix Host header and HSTS rewrites when an explicit `:80` port is included in URL. (Pull #649)
-- Query Params on the URL string are merged with any `params=...` argument. (Pull #653)
-- More robust behavior when closing connections. (Pull #640)
-- More robust behavior when handling HTTP/2 headers with trailing whitespace. (Pull #637)
-- Allow any explicit `Content-Type` header to take precedence over the encoding default. (Pull #633)
-
-## 0.9.4 (December 12th, 2019)
-
-### Fixed
-
-- Added expiry to Keep-Alive connections, resolving issues with acquiring connections. (Pull #627)
-- Increased flow control windows on HTTP/2, resolving download speed issues. (Pull #629)
-
-## 0.9.3 (December 7th, 2019)
-
-### Fixed
-
-- Fixed HTTP/2 with autodetection backend. (Pull #614)
-
-## 0.9.2 (December 7th, 2019)
-
-* Released due to packaging build artifact.
-
-## 0.9.1 (December 6th, 2019)
-
-* Released due to packaging build artifact.
-
-## 0.9.0 (December 6th, 2019)
-
-The 0.9 releases brings some major new features, including:
-
-* A new streaming API.
-* Autodetection of either asyncio or trio.
-* Nicer timeout configuration.
-* HTTP/2 support off by default, but can be enabled.
-
-We've also removed all private types from the top-level package export.
-
-In order to ensure you are only ever working with public API you should make
-sure to only import the top-level package eg. `import httpx`, rather than
-importing modules within the package.
-
-### Added
-
-- Added concurrency backend autodetection. (Pull #585)
-- Added `Client(backend='trio')` and `Client(backend='asyncio')` API. (Pull #585)
-- Added `response.stream_lines()` API. (Pull #575)
-- Added `response.is_error` API. (Pull #574)
-- Added support for `timeout=Timeout(5.0, connect_timeout=60.0)` styles. (Pull #593)
-
-### Fixed
-
-- Requests or Clients with `timeout=None` now correctly always disable timeouts. (Pull #592)
-- Request 'Authorization' headers now have priority over `.netrc` authentication info. (Commit 095b691)
-- Files without a filename no longer set a Content-Type in multipart data. (Commit ed94950)
-
-### Changed
-
-- Added `httpx.stream()` API. Using `stream=True` now results in a warning. (Pull #600, #610)
-- HTTP/2 support is switched to "off by default", but can be enabled explicitly. (Pull #584)
-- Switched to `Client(http2=True)` API from `Client(http_versions=["HTTP/1.1", "HTTP/2"])`. (Pull #586)
-- Removed all private types from the top-level package export. (Pull #608)
-- The SSL configuration settings of `verify`, `cert`, and `trust_env` now raise warnings if used per-request when using a Client instance. They should always be set on the Client instance itself. (Pull #597)
-- Use plain strings "TUNNEL_ONLY" or "FORWARD_ONLY" on the HTTPProxy `proxy_mode` argument. The `HTTPProxyMode` enum still exists, but its usage will raise warnings. (#610)
-- Pool timeouts are now on the timeout configuration, not the pool limits configuration. (Pull #563)
-- The timeout configuration is now named `httpx.Timeout(...)`, not `httpx.TimeoutConfig(...)`. The old version currently remains as a synonym for backwards compatability. (Pull #591)
-
----
-
-## 0.8.0 (November 27, 2019)
-
-### Removed
-
-- The synchronous API has been removed, in order to allow us to fundamentally change how we approach supporting both sync and async variants. (See #588 for more details.)
-
----
-
-## 0.7.8 (November 17, 2019)
-
-### Added
-
-- Add support for proxy tunnels for Python 3.6 + asyncio. (Pull #521)
-
-## 0.7.7 (November 15, 2019)
-
-### Fixed
-
-- Resolve an issue with cookies behavior on redirect requests. (Pull #529)
-
-### Added
-
-- Add request/response DEBUG logs. (Pull #502)
-- Use TRACE log level for low level info. (Pull #500)
-
-## 0.7.6 (November 2, 2019)
-
-### Removed
-
-- Drop `proxies` parameter from the high-level API. (Pull #485)
-
-### Fixed
-
-- Tweak multipart files: omit null filenames, add support for `str` file contents. (Pull #482)
-- Cache NETRC authentication per-client. (Pull #400)
-- Rely on `getproxies` for all proxy environment variables. (Pull #470)
-- Wait for the `asyncio` stream to close when closing a connection. (Pull #494)
-
-## 0.7.5 (October 10, 2019)
-
-### Added
-
-- Allow lists of values to be passed to `params`. (Pull #386)
-- `ASGIDispatch`, `WSGIDispatch` are now available in the `httpx.dispatch` namespace. (Pull #407)
-- `HTTPError` is now available in the `httpx` namespace. (Pull #421)
-- Add support for `start_tls()` to the Trio concurrency backend. (Pull #467)
-
-### Fixed
-
-- Username and password are no longer included in the `Host` header when basic authentication
- credentials are supplied via the URL. (Pull #417)
-
-### Removed
-
-- The `.delete()` function no longer has `json`, `data`, or `files` parameters
- to match the expected semantics of the `DELETE` method. (Pull #408)
-- Removed the `trio` extra. Trio support is detected automatically. (Pull #390)
-
-## 0.7.4 (September 25, 2019)
-
-### Added
-
-- Add Trio concurrency backend. (Pull #276)
-- Add `params` parameter to `Client` for setting default query parameters. (Pull #372)
-- Add support for `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables. (Pull #307)
-- Add debug logging to calls into ASGI apps. (Pull #371)
-- Add debug logging to SSL configuration. (Pull #378)
-
-### Fixed
-
-- Fix a bug when using `Client` without timeouts in Python 3.6. (Pull #383)
-- Propagate `Client` configuration to HTTP proxies. (Pull #377)
-
-## 0.7.3 (September 20, 2019)
-
-### Added
-
-- HTTP Proxy support. (Pulls #259, #353)
-- Add Digest authentication. (Pull #332)
-- Add `.build_request()` method to `Client` and `AsyncClient`. (Pull #319)
-- Add `.elapsed` property on responses. (Pull #351)
-- Add support for `SSLKEYLOGFILE` in Python 3.8b4+. (Pull #301)
-
-### Removed
-
-- Drop NPN support for HTTP version negotiation. (Pull #314)
-
-### Fixed
-
-- Fix distribution of type annotations for mypy (Pull #361).
-- Set `Host` header when redirecting cross-origin. (Pull #321)
-- Drop `Content-Length` headers on `GET` redirects. (Pull #310)
-- Raise `KeyError` if header isn't found in `Headers`. (Pull #324)
-- Raise `NotRedirectResponse` in `response.next()` if there is no redirection to perform. (Pull #297)
-- Fix bug in calculating the HTTP/2 maximum frame size. (Pull #153)
-
-## 0.7.2 (August 28, 2019)
-
-- Enforce using `httpx.AsyncioBackend` for the synchronous client. (Pull #232)
-- `httpx.ConnectionPool` will properly release a dropped connection. (Pull #230)
-- Remove the `raise_app_exceptions` argument from `Client`. (Pull #238)
-- `DecodeError` will no longer be raised for an empty body encoded with Brotli. (Pull #237)
-- Added `http_versions` parameter to `Client`. (Pull #250)
-- Only use HTTP/1.1 on short-lived connections like `httpx.get()`. (Pull #284)
-- Convert `Client.cookies` and `Client.headers` when set as a property. (Pull #274)
-- Setting `HTTPX_DEBUG=1` enables debug logging on all requests. (Pull #277)
-
-## 0.7.1 (August 18, 2019)
-
-- Include files with source distribution to be installable. (Pull #233)
-
-## 0.7.0 (August 17, 2019)
-
-- Add the `trust_env` property to `BaseClient`. (Pull #187)
-- Add the `links` property to `BaseResponse`. (Pull #211)
-- Accept `ssl.SSLContext` instances into `SSLConfig(verify=...)`. (Pull #215)
-- Add `Response.stream_text()` with incremental encoding detection. (Pull #183)
-- Properly updated the `Host` header when a redirect changes the origin. (Pull #199)
-- Ignore invalid `Content-Encoding` headers. (Pull #196)
-- Use `~/.netrc` and `~/_netrc` files by default when `trust_env=True`. (Pull #189)
-- Create exception base class `HTTPError` with `request` and `response` properties. (Pull #162)
-- Add HSTS preload list checking within `BaseClient` to upgrade HTTP URLs to HTTPS. (Pull #184)
-- Switch IDNA encoding from IDNA 2003 to IDNA 2008. (Pull #161)
-- Expose base classes for alternate concurrency backends. (Pull #178)
-- Improve Multipart parameter encoding. (Pull #167)
-- Add the `headers` proeprty to `BaseClient`. (Pull #159)
-- Add support for Google's `brotli` library. (Pull #156)
-- Remove deprecated TLS versions (TLSv1 and TLSv1.1) from default `SSLConfig`. (Pull #155)
-- Fix `URL.join(...)` to work similarly to RFC 3986 URL joining. (Pull #144)
-
----
-
-## 0.6.8 (July 25, 2019)
-
-- Check for disconnections when searching for an available
- connection in `ConnectionPool.keepalive_connections` (Pull #145)
-- Allow string comparison for `URL` objects (Pull #139)
-- Add HTTP status codes 418 and 451 (Pull #135)
-- Add support for client certificate passwords (Pull #118)
-- Enable post-handshake client cert authentication for TLSv1.3 (Pull #118)
-- Disable using `commonName` for hostname checking for OpenSSL 1.1.0+ (Pull #118)
-- Detect encoding for `Response.json()` (Pull #116)
-
-## 0.6.7 (July 8, 2019)
-
-- Check for connection aliveness on re-acquiry (Pull #111)
-
-## 0.6.6 (July 3, 2019)
-
-- Improve `USER_AGENT` (Pull #110)
-- Add `Connection: keep-alive` by default to HTTP/1.1 connections. (Pull #110)
-
-## 0.6.5 (June 27, 2019)
-
-- Include `Host` header by default. (Pull #109)
-- Improve HTTP protocol detection. (Pull #107)
-
-## 0.6.4 (June 25, 2019)
-
-- Implement read and write timeouts (Pull #104)
-
-## 0.6.3 (June 24, 2019)
-
-- Handle early connection closes (Pull #103)
-
-## 0.6.2 (June 23, 2019)
-
-- Use urllib3's `DEFAULT_CIPHERS` for the `SSLConfig` object. (Pull #100)
-
-## 0.6.1 (June 21, 2019)
-
-- Add support for setting a `base_url` on the `Client`.
-
-## 0.6.0 (June 21, 2019)
-
-- Honor `local_flow_control_window` for HTTP/2 connections (Pull #98)
-
-
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/RECORD b/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/RECORD
deleted file mode 100644
index e9acb0d1..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/RECORD
+++ /dev/null
@@ -1,50 +0,0 @@
-httpx-0.19.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-httpx-0.19.0.dist-info/LICENSE.md,sha256=TsWdVE8StfU5o6cW_TIaxYzNgDC0ZSIfLIgCAM3yjY0,1508
-httpx-0.19.0.dist-info/METADATA,sha256=a7mq7nlrwLwsYYiQBI5oLUrVdnjFRVtXZz3ZT0Mpra0,45612
-httpx-0.19.0.dist-info/RECORD,,
-httpx-0.19.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-httpx-0.19.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
-httpx-0.19.0.dist-info/top_level.txt,sha256=8QYqFolXm27kV0x-8K8V5t-uZskSHKtq8jZVxGwtIq4,24
-httpx/__init__.py,sha256=UaP-xFey6dHDXR9KS5XZF9otl_3WNdk_2xGc1pB_7CE,2761
-httpx/__pycache__/__init__.cpython-39.pyc,,
-httpx/__pycache__/__version__.cpython-39.pyc,,
-httpx/__pycache__/_api.cpython-39.pyc,,
-httpx/__pycache__/_auth.cpython-39.pyc,,
-httpx/__pycache__/_client.cpython-39.pyc,,
-httpx/__pycache__/_compat.cpython-39.pyc,,
-httpx/__pycache__/_config.cpython-39.pyc,,
-httpx/__pycache__/_content.cpython-39.pyc,,
-httpx/__pycache__/_decoders.cpython-39.pyc,,
-httpx/__pycache__/_exceptions.cpython-39.pyc,,
-httpx/__pycache__/_models.cpython-39.pyc,,
-httpx/__pycache__/_multipart.cpython-39.pyc,,
-httpx/__pycache__/_status_codes.cpython-39.pyc,,
-httpx/__pycache__/_types.cpython-39.pyc,,
-httpx/__pycache__/_utils.cpython-39.pyc,,
-httpx/__version__.py,sha256=XzEsmr71JIVGLXSchoYB6jqHfy9bLrz53XF5iCCle2k,108
-httpx/_api.py,sha256=HQxn11Qq20DXoSLNDTADpHsNaZZc1LbeQ6UT7dNkkCw,11676
-httpx/_auth.py,sha256=_oB2rvFKngdFpBvFSZKM1k7U1Q4rqRfimCmb7DmtVB0,10242
-httpx/_client.py,sha256=vYrgA06-EFHGIvICPlHRjdzi794UYmF0Kash3TwD9K0,65056
-httpx/_compat.py,sha256=sn1fBUUq7iIxOREBEa9VuDxAKP8kiHORSLI_h3fSi4k,1856
-httpx/_config.py,sha256=eAaNjV4RpAtvk-WzL_mgDx_-Y4gmsaMZMnuuY1vxA-0,11842
-httpx/_content.py,sha256=Z48LbGjD2tLH_oPB1dISGi4tpGWg-ncOngclWJblBGQ,6916
-httpx/_decoders.py,sha256=dz5F-Sud-HFLkdR715RDoqSiSmwi4E2hqmviNpgxNxc,10155
-httpx/_exceptions.py,sha256=MOrPYbCWreCtlgwn1msgaaTrvFBAM6t5GXe4X8ud9aM,7797
-httpx/_models.py,sha256=iU-BJ7eXQ8dmuDClF1ESq38xI6xzeUqs204CAYZoClk,66272
-httpx/_multipart.py,sha256=EB0v22oqGZUc-tZ2_Op72mdIWw7t5gNSS0hwU2VUOfw,6807
-httpx/_status_codes.py,sha256=b4bJYEAu6SsNKx1VhYAaM1UA20h7TyokwU57k3UuCqE,5313
-httpx/_transports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-httpx/_transports/__pycache__/__init__.cpython-39.pyc,,
-httpx/_transports/__pycache__/asgi.cpython-39.pyc,,
-httpx/_transports/__pycache__/base.cpython-39.pyc,,
-httpx/_transports/__pycache__/default.cpython-39.pyc,,
-httpx/_transports/__pycache__/mock.cpython-39.pyc,,
-httpx/_transports/__pycache__/wsgi.cpython-39.pyc,,
-httpx/_transports/asgi.py,sha256=yGmxK-GImAyCRzDUwlX7rFNLeRiohorlJEt2t04_tp0,5189
-httpx/_transports/base.py,sha256=vsxknZSyqLrd0bUTG7xqEjIJUEYyyEJd1QpWGLBd0Hk,6723
-httpx/_transports/default.py,sha256=aE6HQaXJSGL3uASapD3zrEKQDlFG8TF587hdksgR2G0,9461
-httpx/_transports/mock.py,sha256=ITDBS0y8Jg_yTNKXz3SSEnlNRD-c9Yws_I1Xh3JB_Vo,2063
-httpx/_transports/wsgi.py,sha256=954IFakUZse4SH_InSEDgKv2_c37RUUFkiqdMtRC6KI,4481
-httpx/_types.py,sha256=sM2JdaXu7Q3t74SryvYu6sTb1LULi6DdI_SCVJQ1yz4,2202
-httpx/_utils.py,sha256=yen2GFqPpU8VUQ0vuPOwu31XFE4ocsa9FheV6aq4qGs,16568
-httpx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/REQUESTED b/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/REQUESTED
deleted file mode 100644
index e69de29b..00000000
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/WHEEL b/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/WHEEL
deleted file mode 100644
index 5bad85fd..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/WHEEL
+++ /dev/null
@@ -1,5 +0,0 @@
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.37.0)
-Root-Is-Purelib: true
-Tag: py3-none-any
-
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/top_level.txt b/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/top_level.txt
deleted file mode 100644
index c180eb2f..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx-0.19.0.dist-info/top_level.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-httpx
-httpx/_transports
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__init__.py b/IKEA_scraper/.venv/Lib/site-packages/httpx/__init__.py
deleted file mode 100644
index 4af3904f..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx/__init__.py
+++ /dev/null
@@ -1,124 +0,0 @@
-from .__version__ import __description__, __title__, __version__
-from ._api import delete, get, head, options, patch, post, put, request, stream
-from ._auth import Auth, BasicAuth, DigestAuth
-from ._client import USE_CLIENT_DEFAULT, AsyncClient, Client
-from ._config import Limits, Proxy, Timeout, create_ssl_context
-from ._content import ByteStream
-from ._exceptions import (
- CloseError,
- ConnectError,
- ConnectTimeout,
- CookieConflict,
- DecodingError,
- HTTPError,
- HTTPStatusError,
- InvalidURL,
- LocalProtocolError,
- NetworkError,
- PoolTimeout,
- ProtocolError,
- ProxyError,
- ReadError,
- ReadTimeout,
- RemoteProtocolError,
- RequestError,
- RequestNotRead,
- ResponseNotRead,
- StreamClosed,
- StreamConsumed,
- StreamError,
- TimeoutException,
- TooManyRedirects,
- TransportError,
- UnsupportedProtocol,
- WriteError,
- WriteTimeout,
-)
-from ._models import URL, Cookies, Headers, QueryParams, Request, Response
-from ._status_codes import codes
-from ._transports.asgi import ASGITransport
-from ._transports.base import (
- AsyncBaseTransport,
- AsyncByteStream,
- BaseTransport,
- SyncByteStream,
-)
-from ._transports.default import AsyncHTTPTransport, HTTPTransport
-from ._transports.mock import MockTransport
-from ._transports.wsgi import WSGITransport
-
-__all__ = [
- "__description__",
- "__title__",
- "__version__",
- "ASGITransport",
- "AsyncBaseTransport",
- "AsyncByteStream",
- "AsyncClient",
- "AsyncHTTPTransport",
- "Auth",
- "BaseTransport",
- "BasicAuth",
- "ByteStream",
- "Client",
- "CloseError",
- "codes",
- "ConnectError",
- "ConnectTimeout",
- "CookieConflict",
- "Cookies",
- "create_ssl_context",
- "DecodingError",
- "delete",
- "DigestAuth",
- "get",
- "head",
- "Headers",
- "HTTPError",
- "HTTPStatusError",
- "HTTPTransport",
- "InvalidURL",
- "Limits",
- "LocalProtocolError",
- "MockTransport",
- "NetworkError",
- "options",
- "patch",
- "PoolTimeout",
- "post",
- "ProtocolError",
- "Proxy",
- "ProxyError",
- "put",
- "QueryParams",
- "ReadError",
- "ReadTimeout",
- "RemoteProtocolError",
- "request",
- "Request",
- "RequestError",
- "RequestNotRead",
- "Response",
- "ResponseNotRead",
- "stream",
- "StreamClosed",
- "StreamConsumed",
- "StreamError",
- "SyncByteStream",
- "Timeout",
- "TimeoutException",
- "TooManyRedirects",
- "TransportError",
- "UnsupportedProtocol",
- "URL",
- "USE_CLIENT_DEFAULT",
- "WriteError",
- "WriteTimeout",
- "WSGITransport",
-]
-
-
-__locals = locals()
-for __name in __all__:
- if not __name.startswith("__"):
- setattr(__locals[__name], "__module__", "httpx") # noqa
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/__init__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/__init__.cpython-39.pyc
deleted file mode 100644
index 01b0d7e0..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/__init__.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/__version__.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/__version__.cpython-39.pyc
deleted file mode 100644
index 99eb528c..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/__version__.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_api.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_api.cpython-39.pyc
deleted file mode 100644
index 92cc2845..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_api.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_auth.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_auth.cpython-39.pyc
deleted file mode 100644
index b55a9baf..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_auth.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_client.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_client.cpython-39.pyc
deleted file mode 100644
index ba2855e6..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_client.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_compat.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_compat.cpython-39.pyc
deleted file mode 100644
index 653c2ec8..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_compat.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_config.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_config.cpython-39.pyc
deleted file mode 100644
index 21efdac5..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_config.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_content.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_content.cpython-39.pyc
deleted file mode 100644
index 429fa64e..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_content.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_decoders.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_decoders.cpython-39.pyc
deleted file mode 100644
index 42a0744d..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_decoders.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_exceptions.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_exceptions.cpython-39.pyc
deleted file mode 100644
index 8bd0c058..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_exceptions.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_models.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_models.cpython-39.pyc
deleted file mode 100644
index 77cdb4f6..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_models.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_multipart.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_multipart.cpython-39.pyc
deleted file mode 100644
index 937b9945..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_multipart.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_status_codes.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_status_codes.cpython-39.pyc
deleted file mode 100644
index 749673ca..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_status_codes.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_types.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_types.cpython-39.pyc
deleted file mode 100644
index bf3bf66f..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_types.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_utils.cpython-39.pyc b/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_utils.cpython-39.pyc
deleted file mode 100644
index c0bb2716..00000000
Binary files a/IKEA_scraper/.venv/Lib/site-packages/httpx/__pycache__/_utils.cpython-39.pyc and /dev/null differ
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/__version__.py b/IKEA_scraper/.venv/Lib/site-packages/httpx/__version__.py
deleted file mode 100644
index bab8a1c0..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx/__version__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-__title__ = "httpx"
-__description__ = "A next generation HTTP client, for Python 3."
-__version__ = "0.19.0"
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/_api.py b/IKEA_scraper/.venv/Lib/site-packages/httpx/_api.py
deleted file mode 100644
index da818538..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx/_api.py
+++ /dev/null
@@ -1,445 +0,0 @@
-import typing
-from contextlib import contextmanager
-
-from ._client import Client
-from ._config import DEFAULT_TIMEOUT_CONFIG
-from ._models import Response
-from ._types import (
- AuthTypes,
- CertTypes,
- CookieTypes,
- HeaderTypes,
- ProxiesTypes,
- QueryParamTypes,
- RequestContent,
- RequestData,
- RequestFiles,
- TimeoutTypes,
- URLTypes,
- VerifyTypes,
-)
-
-
-def request(
- method: str,
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: AuthTypes = None,
- proxies: ProxiesTypes = None,
- timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
- allow_redirects: bool = True,
- verify: VerifyTypes = True,
- cert: CertTypes = None,
- trust_env: bool = True,
-) -> Response:
- """
- Sends an HTTP request.
-
- **Parameters:**
-
- * **method** - HTTP method for the new `Request` object: `GET`, `OPTIONS`,
- `HEAD`, `POST`, `PUT`, `PATCH`, or `DELETE`.
- * **url** - URL for the new `Request` object.
- * **params** - *(optional)* Query parameters to include in the URL, as a
- string, dictionary, or sequence of two-tuples.
- * **content** - *(optional)* Binary content to include in the body of the
- request, as bytes or a byte iterator.
- * **data** - *(optional)* Form data to include in the body of the request,
- as a dictionary.
- * **files** - *(optional)* A dictionary of upload files to include in the
- body of the request.
- * **json** - *(optional)* A JSON serializable object to include in the body
- of the request.
- * **headers** - *(optional)* Dictionary of HTTP headers to include in the
- request.
- * **cookies** - *(optional)* Dictionary of Cookie items to include in the
- request.
- * **auth** - *(optional)* An authentication class to use when sending the
- request.
- * **proxies** - *(optional)* A dictionary mapping proxy keys to proxy URLs.
- * **timeout** - *(optional)* The timeout configuration to use when sending
- the request.
- * **allow_redirects** - *(optional)* Enables or disables HTTP redirects.
- * **verify** - *(optional)* SSL certificates (a.k.a CA bundle) used to
- verify the identity of requested hosts. Either `True` (default CA bundle),
- a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
- (which will disable verification).
- * **cert** - *(optional)* An SSL certificate used by the requested host
- to authenticate the client. Either a path to an SSL certificate file, or
- two-tuple of (certificate file, key file), or a three-tuple of (certificate
- file, key file, password).
- * **trust_env** - *(optional)* Enables or disables usage of environment
- variables for configuration.
-
- **Returns:** `Response`
-
- Usage:
-
- ```
- >>> import httpx
- >>> response = httpx.request('GET', 'https://httpbin.org/get')
- >>> response
-
- ```
- """
- with Client(
- cookies=cookies,
- proxies=proxies,
- cert=cert,
- verify=verify,
- timeout=timeout,
- trust_env=trust_env,
- ) as client:
- return client.request(
- method=method,
- url=url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- auth=auth,
- allow_redirects=allow_redirects,
- )
-
-
-@contextmanager
-def stream(
- method: str,
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: AuthTypes = None,
- proxies: ProxiesTypes = None,
- timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
- allow_redirects: bool = True,
- verify: VerifyTypes = True,
- cert: CertTypes = None,
- trust_env: bool = True,
-) -> typing.Iterator[Response]:
- """
- Alternative to `httpx.request()` that streams the response body
- instead of loading it into memory at once.
-
- **Parameters**: See `httpx.request`.
-
- See also: [Streaming Responses][0]
-
- [0]: /quickstart#streaming-responses
- """
- with Client(
- cookies=cookies,
- proxies=proxies,
- cert=cert,
- verify=verify,
- timeout=timeout,
- trust_env=trust_env,
- ) as client:
- with client.stream(
- method=method,
- url=url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- auth=auth,
- allow_redirects=allow_redirects,
- ) as response:
- yield response
-
-
-def get(
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: AuthTypes = None,
- proxies: ProxiesTypes = None,
- allow_redirects: bool = True,
- cert: CertTypes = None,
- verify: VerifyTypes = True,
- timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
- trust_env: bool = True,
-) -> Response:
- """
- Sends a `GET` request.
-
- **Parameters**: See `httpx.request`.
-
- Note that the `data`, `files`, and `json` parameters are not available on
- this function, as `GET` requests should not include a request body.
- """
- return request(
- "GET",
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- proxies=proxies,
- allow_redirects=allow_redirects,
- cert=cert,
- verify=verify,
- timeout=timeout,
- trust_env=trust_env,
- )
-
-
-def options(
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: AuthTypes = None,
- proxies: ProxiesTypes = None,
- allow_redirects: bool = True,
- cert: CertTypes = None,
- verify: VerifyTypes = True,
- timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
- trust_env: bool = True,
-) -> Response:
- """
- Sends an `OPTIONS` request.
-
- **Parameters**: See `httpx.request`.
-
- Note that the `data`, `files`, and `json` parameters are not available on
- this function, as `OPTIONS` requests should not include a request body.
- """
- return request(
- "OPTIONS",
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- proxies=proxies,
- allow_redirects=allow_redirects,
- cert=cert,
- verify=verify,
- timeout=timeout,
- trust_env=trust_env,
- )
-
-
-def head(
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: AuthTypes = None,
- proxies: ProxiesTypes = None,
- allow_redirects: bool = True,
- cert: CertTypes = None,
- verify: VerifyTypes = True,
- timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
- trust_env: bool = True,
-) -> Response:
- """
- Sends a `HEAD` request.
-
- **Parameters**: See `httpx.request`.
-
- Note that the `data`, `files`, and `json` parameters are not available on
- this function, as `HEAD` requests should not include a request body.
- """
- return request(
- "HEAD",
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- proxies=proxies,
- allow_redirects=allow_redirects,
- cert=cert,
- verify=verify,
- timeout=timeout,
- trust_env=trust_env,
- )
-
-
-def post(
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: AuthTypes = None,
- proxies: ProxiesTypes = None,
- allow_redirects: bool = True,
- cert: CertTypes = None,
- verify: VerifyTypes = True,
- timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
- trust_env: bool = True,
-) -> Response:
- """
- Sends a `POST` request.
-
- **Parameters**: See `httpx.request`.
- """
- return request(
- "POST",
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- proxies=proxies,
- allow_redirects=allow_redirects,
- cert=cert,
- verify=verify,
- timeout=timeout,
- trust_env=trust_env,
- )
-
-
-def put(
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: AuthTypes = None,
- proxies: ProxiesTypes = None,
- allow_redirects: bool = True,
- cert: CertTypes = None,
- verify: VerifyTypes = True,
- timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
- trust_env: bool = True,
-) -> Response:
- """
- Sends a `PUT` request.
-
- **Parameters**: See `httpx.request`.
- """
- return request(
- "PUT",
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- proxies=proxies,
- allow_redirects=allow_redirects,
- cert=cert,
- verify=verify,
- timeout=timeout,
- trust_env=trust_env,
- )
-
-
-def patch(
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: AuthTypes = None,
- proxies: ProxiesTypes = None,
- allow_redirects: bool = True,
- cert: CertTypes = None,
- verify: VerifyTypes = True,
- timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
- trust_env: bool = True,
-) -> Response:
- """
- Sends a `PATCH` request.
-
- **Parameters**: See `httpx.request`.
- """
- return request(
- "PATCH",
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- proxies=proxies,
- allow_redirects=allow_redirects,
- cert=cert,
- verify=verify,
- timeout=timeout,
- trust_env=trust_env,
- )
-
-
-def delete(
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: AuthTypes = None,
- proxies: ProxiesTypes = None,
- allow_redirects: bool = True,
- cert: CertTypes = None,
- verify: VerifyTypes = True,
- timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
- trust_env: bool = True,
-) -> Response:
- """
- Sends a `DELETE` request.
-
- **Parameters**: See `httpx.request`.
-
- Note that the `data`, `files`, and `json` parameters are not available on
- this function, as `DELETE` requests should not include a request body.
- """
- return request(
- "DELETE",
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- proxies=proxies,
- allow_redirects=allow_redirects,
- cert=cert,
- verify=verify,
- timeout=timeout,
- trust_env=trust_env,
- )
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/_auth.py b/IKEA_scraper/.venv/Lib/site-packages/httpx/_auth.py
deleted file mode 100644
index 343f9cdd..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx/_auth.py
+++ /dev/null
@@ -1,304 +0,0 @@
-import hashlib
-import os
-import re
-import time
-import typing
-from base64 import b64encode
-from urllib.request import parse_http_list
-
-from ._exceptions import ProtocolError
-from ._models import Request, Response
-from ._utils import to_bytes, to_str, unquote
-
-
-class Auth:
- """
- Base class for all authentication schemes.
-
- To implement a custom authentication scheme, subclass `Auth` and override
- the `.auth_flow()` method.
-
- If the authentication scheme does I/O such as disk access or network calls, or uses
- synchronization primitives such as locks, you should override `.sync_auth_flow()`
- and/or `.async_auth_flow()` instead of `.auth_flow()` to provide specialized
- implementations that will be used by `Client` and `AsyncClient` respectively.
- """
-
- requires_request_body = False
- requires_response_body = False
-
- def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]:
- """
- Execute the authentication flow.
-
- To dispatch a request, `yield` it:
-
- ```
- yield request
- ```
-
- The client will `.send()` the response back into the flow generator. You can
- access it like so:
-
- ```
- response = yield request
- ```
-
- A `return` (or reaching the end of the generator) will result in the
- client returning the last response obtained from the server.
-
- You can dispatch as many requests as is necessary.
- """
- yield request
-
- def sync_auth_flow(
- self, request: Request
- ) -> typing.Generator[Request, Response, None]:
- """
- Execute the authentication flow synchronously.
-
- By default, this defers to `.auth_flow()`. You should override this method
- when the authentication scheme does I/O and/or uses concurrency primitives.
- """
- if self.requires_request_body:
- request.read()
-
- flow = self.auth_flow(request)
- request = next(flow)
-
- while True:
- response = yield request
- if self.requires_response_body:
- response.read()
-
- try:
- request = flow.send(response)
- except StopIteration:
- break
-
- async def async_auth_flow(
- self, request: Request
- ) -> typing.AsyncGenerator[Request, Response]:
- """
- Execute the authentication flow asynchronously.
-
- By default, this defers to `.auth_flow()`. You should override this method
- when the authentication scheme does I/O and/or uses concurrency primitives.
- """
- if self.requires_request_body:
- await request.aread()
-
- flow = self.auth_flow(request)
- request = next(flow)
-
- while True:
- response = yield request
- if self.requires_response_body:
- await response.aread()
-
- try:
- request = flow.send(response)
- except StopIteration:
- break
-
-
-class FunctionAuth(Auth):
- """
- Allows the 'auth' argument to be passed as a simple callable function,
- that takes the request, and returns a new, modified request.
- """
-
- def __init__(self, func: typing.Callable[[Request], Request]) -> None:
- self._func = func
-
- def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]:
- yield self._func(request)
-
-
-class BasicAuth(Auth):
- """
- Allows the 'auth' argument to be passed as a (username, password) pair,
- and uses HTTP Basic authentication.
- """
-
- def __init__(
- self, username: typing.Union[str, bytes], password: typing.Union[str, bytes]
- ):
- self._auth_header = self._build_auth_header(username, password)
-
- def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]:
- request.headers["Authorization"] = self._auth_header
- yield request
-
- def _build_auth_header(
- self, username: typing.Union[str, bytes], password: typing.Union[str, bytes]
- ) -> str:
- userpass = b":".join((to_bytes(username), to_bytes(password)))
- token = b64encode(userpass).decode()
- return f"Basic {token}"
-
-
-class DigestAuth(Auth):
- _ALGORITHM_TO_HASH_FUNCTION: typing.Dict[str, typing.Callable] = {
- "MD5": hashlib.md5,
- "MD5-SESS": hashlib.md5,
- "SHA": hashlib.sha1,
- "SHA-SESS": hashlib.sha1,
- "SHA-256": hashlib.sha256,
- "SHA-256-SESS": hashlib.sha256,
- "SHA-512": hashlib.sha512,
- "SHA-512-SESS": hashlib.sha512,
- }
-
- def __init__(
- self, username: typing.Union[str, bytes], password: typing.Union[str, bytes]
- ) -> None:
- self._username = to_bytes(username)
- self._password = to_bytes(password)
-
- def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]:
- response = yield request
-
- if response.status_code != 401 or "www-authenticate" not in response.headers:
- # If the response is not a 401 then we don't
- # need to build an authenticated request.
- return
-
- for auth_header in response.headers.get_list("www-authenticate"):
- if auth_header.lower().startswith("digest "):
- break
- else:
- # If the response does not include a 'WWW-Authenticate: Digest ...'
- # header, then we don't need to build an authenticated request.
- return
-
- challenge = self._parse_challenge(request, response, auth_header)
- request.headers["Authorization"] = self._build_auth_header(request, challenge)
- yield request
-
- def _parse_challenge(
- self, request: Request, response: Response, auth_header: str
- ) -> "_DigestAuthChallenge":
- """
- Returns a challenge from a Digest WWW-Authenticate header.
- These take the form of:
- `Digest realm="realm@host.com",qop="auth,auth-int",nonce="abc",opaque="xyz"`
- """
- scheme, _, fields = auth_header.partition(" ")
-
- # This method should only ever have been called with a Digest auth header.
- assert scheme.lower() == "digest"
-
- header_dict: typing.Dict[str, str] = {}
- for field in parse_http_list(fields):
- key, value = field.strip().split("=", 1)
- header_dict[key] = unquote(value)
-
- try:
- realm = header_dict["realm"].encode()
- nonce = header_dict["nonce"].encode()
- algorithm = header_dict.get("algorithm", "MD5")
- opaque = header_dict["opaque"].encode() if "opaque" in header_dict else None
- qop = header_dict["qop"].encode() if "qop" in header_dict else None
- return _DigestAuthChallenge(
- realm=realm, nonce=nonce, algorithm=algorithm, opaque=opaque, qop=qop
- )
- except KeyError as exc:
- message = "Malformed Digest WWW-Authenticate header"
- raise ProtocolError(message, request=request) from exc
-
- def _build_auth_header(
- self, request: Request, challenge: "_DigestAuthChallenge"
- ) -> str:
- hash_func = self._ALGORITHM_TO_HASH_FUNCTION[challenge.algorithm]
-
- def digest(data: bytes) -> bytes:
- return hash_func(data).hexdigest().encode()
-
- A1 = b":".join((self._username, challenge.realm, self._password))
-
- path = request.url.raw_path
- A2 = b":".join((request.method.encode(), path))
- # TODO: implement auth-int
- HA2 = digest(A2)
-
- nonce_count = 1 # TODO: implement nonce counting
- nc_value = b"%08x" % nonce_count
- cnonce = self._get_client_nonce(nonce_count, challenge.nonce)
-
- HA1 = digest(A1)
- if challenge.algorithm.lower().endswith("-sess"):
- HA1 = digest(b":".join((HA1, challenge.nonce, cnonce)))
-
- qop = self._resolve_qop(challenge.qop, request=request)
- if qop is None:
- digest_data = [HA1, challenge.nonce, HA2]
- else:
- digest_data = [challenge.nonce, nc_value, cnonce, qop, HA2]
- key_digest = b":".join(digest_data)
-
- format_args = {
- "username": self._username,
- "realm": challenge.realm,
- "nonce": challenge.nonce,
- "uri": path,
- "response": digest(b":".join((HA1, key_digest))),
- "algorithm": challenge.algorithm.encode(),
- }
- if challenge.opaque:
- format_args["opaque"] = challenge.opaque
- if qop:
- format_args["qop"] = b"auth"
- format_args["nc"] = nc_value
- format_args["cnonce"] = cnonce
-
- return "Digest " + self._get_header_value(format_args)
-
- def _get_client_nonce(self, nonce_count: int, nonce: bytes) -> bytes:
- s = str(nonce_count).encode()
- s += nonce
- s += time.ctime().encode()
- s += os.urandom(8)
-
- return hashlib.sha1(s).hexdigest()[:16].encode()
-
- def _get_header_value(self, header_fields: typing.Dict[str, bytes]) -> str:
- NON_QUOTED_FIELDS = ("algorithm", "qop", "nc")
- QUOTED_TEMPLATE = '{}="{}"'
- NON_QUOTED_TEMPLATE = "{}={}"
-
- header_value = ""
- for i, (field, value) in enumerate(header_fields.items()):
- if i > 0:
- header_value += ", "
- template = (
- QUOTED_TEMPLATE
- if field not in NON_QUOTED_FIELDS
- else NON_QUOTED_TEMPLATE
- )
- header_value += template.format(field, to_str(value))
-
- return header_value
-
- def _resolve_qop(
- self, qop: typing.Optional[bytes], request: Request
- ) -> typing.Optional[bytes]:
- if qop is None:
- return None
- qops = re.split(b", ?", qop)
- if b"auth" in qops:
- return b"auth"
-
- if qops == [b"auth-int"]:
- raise NotImplementedError("Digest auth-int support is not yet implemented")
-
- message = f'Unexpected qop value "{qop!r}" in digest auth'
- raise ProtocolError(message, request=request)
-
-
-class _DigestAuthChallenge(typing.NamedTuple):
- realm: bytes
- nonce: bytes
- algorithm: str
- opaque: typing.Optional[bytes]
- qop: typing.Optional[bytes]
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/_client.py b/IKEA_scraper/.venv/Lib/site-packages/httpx/_client.py
deleted file mode 100644
index 9afe8132..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx/_client.py
+++ /dev/null
@@ -1,2007 +0,0 @@
-import datetime
-import enum
-import typing
-import warnings
-from contextlib import contextmanager
-from types import TracebackType
-
-from .__version__ import __version__
-from ._auth import Auth, BasicAuth, FunctionAuth
-from ._compat import asynccontextmanager
-from ._config import (
- DEFAULT_LIMITS,
- DEFAULT_MAX_REDIRECTS,
- DEFAULT_TIMEOUT_CONFIG,
- Limits,
- Proxy,
- Timeout,
-)
-from ._decoders import SUPPORTED_DECODERS
-from ._exceptions import (
- InvalidURL,
- RemoteProtocolError,
- TooManyRedirects,
- request_context,
-)
-from ._models import URL, Cookies, Headers, QueryParams, Request, Response
-from ._status_codes import codes
-from ._transports.asgi import ASGITransport
-from ._transports.base import (
- AsyncBaseTransport,
- AsyncByteStream,
- BaseTransport,
- SyncByteStream,
-)
-from ._transports.default import AsyncHTTPTransport, HTTPTransport
-from ._transports.wsgi import WSGITransport
-from ._types import (
- AuthTypes,
- CertTypes,
- CookieTypes,
- HeaderTypes,
- ProxiesTypes,
- QueryParamTypes,
- RequestContent,
- RequestData,
- RequestFiles,
- TimeoutTypes,
- URLTypes,
- VerifyTypes,
-)
-from ._utils import (
- NetRCInfo,
- Timer,
- URLPattern,
- get_environment_proxies,
- get_logger,
- same_origin,
-)
-
-# The type annotation for @classmethod and context managers here follows PEP 484
-# https://www.python.org/dev/peps/pep-0484/#annotating-instance-and-class-methods
-T = typing.TypeVar("T", bound="Client")
-U = typing.TypeVar("U", bound="AsyncClient")
-
-
-class UseClientDefault:
- """
- For some parameters such as `auth=...` and `timeout=...` we need to be able
- to indicate the default "unset" state, in a way that is distinctly different
- to using `None`.
-
- The default "unset" state indicates that whatever default is set on the
- client should be used. This is different to setting `None`, which
- explicitly disables the parameter, possibly overriding a client default.
-
- For example we use `timeout=USE_CLIENT_DEFAULT` in the `request()` signature.
- Omitting the `timeout` parameter will send a request using whatever default
- timeout has been configured on the client. Including `timeout=None` will
- ensure no timeout is used.
-
- Note that user code shouldn't need to use the `USE_CLIENT_DEFAULT` constant,
- but it is used internally when a parameter is not included.
- """
-
- pass # pragma: nocover
-
-
-USE_CLIENT_DEFAULT = UseClientDefault()
-
-
-logger = get_logger(__name__)
-
-USER_AGENT = f"python-httpx/{__version__}"
-ACCEPT_ENCODING = ", ".join(
- [key for key in SUPPORTED_DECODERS.keys() if key != "identity"]
-)
-
-
-class ClientState(enum.Enum):
- # UNOPENED:
- # The client has been instantiated, but has not been used to send a request,
- # or been opened by entering the context of a `with` block.
- UNOPENED = 1
- # OPENED:
- # The client has either sent a request, or is within a `with` block.
- OPENED = 2
- # CLOSED:
- # The client has either exited the `with` block, or `close()` has
- # been called explicitly.
- CLOSED = 3
-
-
-class BoundSyncStream(SyncByteStream):
- """
- A byte stream that is bound to a given response instance, and that
- ensures the `response.elapsed` is set once the response is closed.
- """
-
- def __init__(
- self, stream: SyncByteStream, response: Response, timer: Timer
- ) -> None:
- self._stream = stream
- self._response = response
- self._timer = timer
-
- def __iter__(self) -> typing.Iterator[bytes]:
- for chunk in self._stream:
- yield chunk
-
- def close(self) -> None:
- seconds = self._timer.sync_elapsed()
- self._response.elapsed = datetime.timedelta(seconds=seconds)
- self._stream.close()
-
-
-class BoundAsyncStream(AsyncByteStream):
- """
- An async byte stream that is bound to a given response instance, and that
- ensures the `response.elapsed` is set once the response is closed.
- """
-
- def __init__(
- self, stream: AsyncByteStream, response: Response, timer: Timer
- ) -> None:
- self._stream = stream
- self._response = response
- self._timer = timer
-
- async def __aiter__(self) -> typing.AsyncIterator[bytes]:
- async for chunk in self._stream:
- yield chunk
-
- async def aclose(self) -> None:
- seconds = await self._timer.async_elapsed()
- self._response.elapsed = datetime.timedelta(seconds=seconds)
- await self._stream.aclose()
-
-
-class BaseClient:
- def __init__(
- self,
- *,
- auth: AuthTypes = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
- allow_redirects: bool = True,
- max_redirects: int = DEFAULT_MAX_REDIRECTS,
- event_hooks: typing.Mapping[str, typing.List[typing.Callable]] = None,
- base_url: URLTypes = "",
- trust_env: bool = True,
- ):
- event_hooks = {} if event_hooks is None else event_hooks
-
- self._base_url = self._enforce_trailing_slash(URL(base_url))
-
- self._auth = self._build_auth(auth)
- self._params = QueryParams(params)
- self.headers = Headers(headers)
- self._cookies = Cookies(cookies)
- self._timeout = Timeout(timeout)
- self.allow_redirects = allow_redirects
- self.max_redirects = max_redirects
- self._event_hooks = {
- "request": list(event_hooks.get("request", [])),
- "response": list(event_hooks.get("response", [])),
- }
- self._trust_env = trust_env
- self._netrc = NetRCInfo()
- self._state = ClientState.UNOPENED
-
- @property
- def is_closed(self) -> bool:
- """
- Check if the client being closed
- """
- return self._state == ClientState.CLOSED
-
- @property
- def trust_env(self) -> bool:
- return self._trust_env
-
- def _enforce_trailing_slash(self, url: URL) -> URL:
- if url.raw_path.endswith(b"/"):
- return url
- return url.copy_with(raw_path=url.raw_path + b"/")
-
- def _get_proxy_map(
- self, proxies: typing.Optional[ProxiesTypes], allow_env_proxies: bool
- ) -> typing.Dict[str, typing.Optional[Proxy]]:
- if proxies is None:
- if allow_env_proxies:
- return {
- key: None if url is None else Proxy(url=url)
- for key, url in get_environment_proxies().items()
- }
- return {}
- if isinstance(proxies, dict):
- new_proxies = {}
- for key, value in proxies.items():
- proxy = Proxy(url=value) if isinstance(value, (str, URL)) else value
- new_proxies[str(key)] = proxy
- return new_proxies
- else:
- proxy = Proxy(url=proxies) if isinstance(proxies, (str, URL)) else proxies
- return {"all://": proxy}
-
- @property
- def timeout(self) -> Timeout:
- return self._timeout
-
- @timeout.setter
- def timeout(self, timeout: TimeoutTypes) -> None:
- self._timeout = Timeout(timeout)
-
- @property
- def event_hooks(self) -> typing.Dict[str, typing.List[typing.Callable]]:
- return self._event_hooks
-
- @event_hooks.setter
- def event_hooks(
- self, event_hooks: typing.Dict[str, typing.List[typing.Callable]]
- ) -> None:
- self._event_hooks = {
- "request": list(event_hooks.get("request", [])),
- "response": list(event_hooks.get("response", [])),
- }
-
- @property
- def auth(self) -> typing.Optional[Auth]:
- """
- Authentication class used when none is passed at the request-level.
-
- See also [Authentication][0].
-
- [0]: /quickstart/#authentication
- """
- return self._auth
-
- @auth.setter
- def auth(self, auth: AuthTypes) -> None:
- self._auth = self._build_auth(auth)
-
- @property
- def base_url(self) -> URL:
- """
- Base URL to use when sending requests with relative URLs.
- """
- return self._base_url
-
- @base_url.setter
- def base_url(self, url: URLTypes) -> None:
- self._base_url = self._enforce_trailing_slash(URL(url))
-
- @property
- def headers(self) -> Headers:
- """
- HTTP headers to include when sending requests.
- """
- return self._headers
-
- @headers.setter
- def headers(self, headers: HeaderTypes) -> None:
- client_headers = Headers(
- {
- b"Accept": b"*/*",
- b"Accept-Encoding": ACCEPT_ENCODING.encode("ascii"),
- b"Connection": b"keep-alive",
- b"User-Agent": USER_AGENT.encode("ascii"),
- }
- )
- client_headers.update(headers)
- self._headers = client_headers
-
- @property
- def cookies(self) -> Cookies:
- """
- Cookie values to include when sending requests.
- """
- return self._cookies
-
- @cookies.setter
- def cookies(self, cookies: CookieTypes) -> None:
- self._cookies = Cookies(cookies)
-
- @property
- def params(self) -> QueryParams:
- """
- Query parameters to include in the URL when sending requests.
- """
- return self._params
-
- @params.setter
- def params(self, params: QueryParamTypes) -> None:
- self._params = QueryParams(params)
-
- def build_request(
- self,
- method: str,
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- ) -> Request:
- """
- Build and return a request instance.
-
- * The `params`, `headers` and `cookies` arguments
- are merged with any values set on the client.
- * The `url` argument is merged with any `base_url` set on the client.
-
- See also: [Request instances][0]
-
- [0]: /advanced/#request-instances
- """
- url = self._merge_url(url)
- headers = self._merge_headers(headers)
- cookies = self._merge_cookies(cookies)
- params = self._merge_queryparams(params)
- return Request(
- method,
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- )
-
- def _merge_url(self, url: URLTypes) -> URL:
- """
- Merge a URL argument together with any 'base_url' on the client,
- to create the URL used for the outgoing request.
- """
- merge_url = URL(url)
- if merge_url.is_relative_url:
- # To merge URLs we always append to the base URL. To get this
- # behaviour correct we always ensure the base URL ends in a '/'
- # seperator, and strip any leading '/' from the merge URL.
- #
- # So, eg...
- #
- # >>> client = Client(base_url="https://www.example.com/subpath")
- # >>> client.base_url
- # URL('https://www.example.com/subpath/')
- # >>> client.build_request("GET", "/path").url
- # URL('https://www.example.com/subpath/path')
- merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/")
- return self.base_url.copy_with(raw_path=merge_raw_path)
- return merge_url
-
- def _merge_cookies(
- self, cookies: CookieTypes = None
- ) -> typing.Optional[CookieTypes]:
- """
- Merge a cookies argument together with any cookies on the client,
- to create the cookies used for the outgoing request.
- """
- if cookies or self.cookies:
- merged_cookies = Cookies(self.cookies)
- merged_cookies.update(cookies)
- return merged_cookies
- return cookies
-
- def _merge_headers(
- self, headers: HeaderTypes = None
- ) -> typing.Optional[HeaderTypes]:
- """
- Merge a headers argument together with any headers on the client,
- to create the headers used for the outgoing request.
- """
- merged_headers = Headers(self.headers)
- merged_headers.update(headers)
- return merged_headers
-
- def _merge_queryparams(
- self, params: QueryParamTypes = None
- ) -> typing.Optional[QueryParamTypes]:
- """
- Merge a queryparams argument together with any queryparams on the client,
- to create the queryparams used for the outgoing request.
- """
- if params or self.params:
- merged_queryparams = QueryParams(self.params)
- merged_queryparams = merged_queryparams.merge(params)
- return merged_queryparams
- return params
-
- def _build_auth(self, auth: AuthTypes) -> typing.Optional[Auth]:
- if auth is None:
- return None
- elif isinstance(auth, tuple):
- return BasicAuth(username=auth[0], password=auth[1])
- elif isinstance(auth, Auth):
- return auth
- elif callable(auth):
- return FunctionAuth(func=auth)
- else:
- raise TypeError(f'Invalid "auth" argument: {auth!r}')
-
- def _build_request_auth(
- self,
- request: Request,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Auth:
- auth = (
- self._auth if isinstance(auth, UseClientDefault) else self._build_auth(auth)
- )
-
- if auth is not None:
- return auth
-
- username, password = request.url.username, request.url.password
- if username or password:
- return BasicAuth(username=username, password=password)
-
- if self.trust_env and "Authorization" not in request.headers:
- credentials = self._netrc.get_credentials(request.url.host)
- if credentials is not None:
- return BasicAuth(username=credentials[0], password=credentials[1])
-
- return Auth()
-
- def _build_redirect_request(self, request: Request, response: Response) -> Request:
- """
- Given a request and a redirect response, return a new request that
- should be used to effect the redirect.
- """
- method = self._redirect_method(request, response)
- url = self._redirect_url(request, response)
- headers = self._redirect_headers(request, url, method)
- stream = self._redirect_stream(request, method)
- cookies = Cookies(self.cookies)
- return Request(
- method=method, url=url, headers=headers, cookies=cookies, stream=stream
- )
-
- def _redirect_method(self, request: Request, response: Response) -> str:
- """
- When being redirected we may want to change the method of the request
- based on certain specs or browser behavior.
- """
- method = request.method
-
- # https://tools.ietf.org/html/rfc7231#section-6.4.4
- if response.status_code == codes.SEE_OTHER and method != "HEAD":
- method = "GET"
-
- # Do what the browsers do, despite standards...
- # Turn 302s into GETs.
- if response.status_code == codes.FOUND and method != "HEAD":
- method = "GET"
-
- # If a POST is responded to with a 301, turn it into a GET.
- # This bizarre behaviour is explained in 'requests' issue 1704.
- if response.status_code == codes.MOVED_PERMANENTLY and method == "POST":
- method = "GET"
-
- return method
-
- def _redirect_url(self, request: Request, response: Response) -> URL:
- """
- Return the URL for the redirect to follow.
- """
- location = response.headers["Location"]
-
- try:
- url = URL(location)
- except InvalidURL as exc:
- raise RemoteProtocolError(
- f"Invalid URL in location header: {exc}.", request=request
- ) from None
-
- # Handle malformed 'Location' headers that are "absolute" form, have no host.
- # See: https://github.com/encode/httpx/issues/771
- if url.scheme and not url.host:
- url = url.copy_with(host=request.url.host)
-
- # Facilitate relative 'Location' headers, as allowed by RFC 7231.
- # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
- if url.is_relative_url:
- url = request.url.join(url)
-
- # Attach previous fragment if needed (RFC 7231 7.1.2)
- if request.url.fragment and not url.fragment:
- url = url.copy_with(fragment=request.url.fragment)
-
- return url
-
- def _redirect_headers(self, request: Request, url: URL, method: str) -> Headers:
- """
- Return the headers that should be used for the redirect request.
- """
- headers = Headers(request.headers)
-
- if not same_origin(url, request.url):
- # Strip Authorization headers when responses are redirected away from
- # the origin.
- headers.pop("Authorization", None)
-
- # Update the Host header.
- headers["Host"] = url.netloc.decode("ascii")
-
- if method != request.method and method == "GET":
- # If we've switch to a 'GET' request, then strip any headers which
- # are only relevant to the request body.
- headers.pop("Content-Length", None)
- headers.pop("Transfer-Encoding", None)
-
- # We should use the client cookie store to determine any cookie header,
- # rather than whatever was on the original outgoing request.
- headers.pop("Cookie", None)
-
- return headers
-
- def _redirect_stream(
- self, request: Request, method: str
- ) -> typing.Optional[typing.Union[SyncByteStream, AsyncByteStream]]:
- """
- Return the body that should be used for the redirect request.
- """
- if method != request.method and method == "GET":
- return None
-
- return request.stream
-
-
-class Client(BaseClient):
- """
- An HTTP client, with connection pooling, HTTP/2, redirects, cookie persistence, etc.
-
- Usage:
-
- ```python
- >>> client = httpx.Client()
- >>> response = client.get('https://example.org')
- ```
-
- **Parameters:**
-
- * **auth** - *(optional)* An authentication class to use when sending
- requests.
- * **params** - *(optional)* Query parameters to include in request URLs, as
- a string, dictionary, or sequence of two-tuples.
- * **headers** - *(optional)* Dictionary of HTTP headers to include when
- sending requests.
- * **cookies** - *(optional)* Dictionary of Cookie items to include when
- sending requests.
- * **verify** - *(optional)* SSL certificates (a.k.a CA bundle) used to
- verify the identity of requested hosts. Either `True` (default CA bundle),
- a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
- (which will disable verification).
- * **cert** - *(optional)* An SSL certificate used by the requested host
- to authenticate the client. Either a path to an SSL certificate file, or
- two-tuple of (certificate file, key file), or a three-tuple of (certificate
- file, key file, password).
- * **proxies** - *(optional)* A dictionary mapping proxy keys to proxy
- URLs.
- * **timeout** - *(optional)* The timeout configuration to use when sending
- requests.
- * **limits** - *(optional)* The limits configuration to use.
- * **max_redirects** - *(optional)* The maximum number of redirect responses
- that should be followed.
- * **base_url** - *(optional)* A URL to use as the base when building
- request URLs.
- * **transport** - *(optional)* A transport class to use for sending requests
- over the network.
- * **app** - *(optional)* An WSGI application to send requests to,
- rather than sending actual network requests.
- * **trust_env** - *(optional)* Enables or disables usage of environment
- variables for configuration.
- """
-
- def __init__(
- self,
- *,
- auth: AuthTypes = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- verify: VerifyTypes = True,
- cert: CertTypes = None,
- http1: bool = True,
- http2: bool = False,
- proxies: ProxiesTypes = None,
- mounts: typing.Mapping[str, BaseTransport] = None,
- timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
- limits: Limits = DEFAULT_LIMITS,
- max_redirects: int = DEFAULT_MAX_REDIRECTS,
- event_hooks: typing.Mapping[str, typing.List[typing.Callable]] = None,
- base_url: URLTypes = "",
- transport: BaseTransport = None,
- app: typing.Callable = None,
- trust_env: bool = True,
- ):
- super().__init__(
- auth=auth,
- params=params,
- headers=headers,
- cookies=cookies,
- timeout=timeout,
- max_redirects=max_redirects,
- event_hooks=event_hooks,
- base_url=base_url,
- trust_env=trust_env,
- )
-
- if http2:
- try:
- import h2 # noqa
- except ImportError: # pragma: nocover
- raise ImportError(
- "Using http2=True, but the 'h2' package is not installed. "
- "Make sure to install httpx using `pip install httpx[http2]`."
- ) from None
-
- allow_env_proxies = trust_env and app is None and transport is None
- proxy_map = self._get_proxy_map(proxies, allow_env_proxies)
-
- self._transport = self._init_transport(
- verify=verify,
- cert=cert,
- http1=http1,
- http2=http2,
- limits=limits,
- transport=transport,
- app=app,
- trust_env=trust_env,
- )
- self._mounts: typing.Dict[URLPattern, typing.Optional[BaseTransport]] = {
- URLPattern(key): None
- if proxy is None
- else self._init_proxy_transport(
- proxy,
- verify=verify,
- cert=cert,
- http1=http1,
- http2=http2,
- limits=limits,
- trust_env=trust_env,
- )
- for key, proxy in proxy_map.items()
- }
- if mounts is not None:
- self._mounts.update(
- {URLPattern(key): transport for key, transport in mounts.items()}
- )
-
- self._mounts = dict(sorted(self._mounts.items()))
-
- def _init_transport(
- self,
- verify: VerifyTypes = True,
- cert: CertTypes = None,
- http1: bool = True,
- http2: bool = False,
- limits: Limits = DEFAULT_LIMITS,
- transport: BaseTransport = None,
- app: typing.Callable = None,
- trust_env: bool = True,
- ) -> BaseTransport:
- if transport is not None:
- return transport
-
- if app is not None:
- return WSGITransport(app=app)
-
- return HTTPTransport(
- verify=verify,
- cert=cert,
- http1=http1,
- http2=http2,
- limits=limits,
- trust_env=trust_env,
- )
-
- def _init_proxy_transport(
- self,
- proxy: Proxy,
- verify: VerifyTypes = True,
- cert: CertTypes = None,
- http1: bool = True,
- http2: bool = False,
- limits: Limits = DEFAULT_LIMITS,
- trust_env: bool = True,
- ) -> BaseTransport:
- return HTTPTransport(
- verify=verify,
- cert=cert,
- http1=http1,
- http2=http2,
- limits=limits,
- trust_env=trust_env,
- proxy=proxy,
- )
-
- def _transport_for_url(self, url: URL) -> BaseTransport:
- """
- Returns the transport instance that should be used for a given URL.
- This will either be the standard connection pool, or a proxy.
- """
- for pattern, transport in self._mounts.items():
- if pattern.matches(url):
- return self._transport if transport is None else transport
-
- return self._transport
-
- def request(
- self,
- method: str,
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Build and send a request.
-
- Equivalent to:
-
- ```python
- request = client.build_request(...)
- response = client.send(request, ...)
- ```
-
- See `Client.build_request()`, `Client.send()` and
- [Merging of configuration][0] for how the various parameters
- are merged with client-level configuration.
-
- [0]: /advanced/#merging-of-configuration
- """
- if cookies is not None:
- message = (
- "Setting per-request cookies=<...> is being deprecated, because "
- "the expected behaviour on cookie persistence is ambiguous. Set "
- "cookies directly on the client instance instead."
- )
- warnings.warn(message, DeprecationWarning)
-
- request = self.build_request(
- method=method,
- url=url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- )
- return self.send(
- request, auth=auth, allow_redirects=allow_redirects, timeout=timeout
- )
-
- @contextmanager
- def stream(
- self,
- method: str,
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> typing.Iterator[Response]:
- """
- Alternative to `httpx.request()` that streams the response body
- instead of loading it into memory at once.
-
- **Parameters**: See `httpx.request`.
-
- See also: [Streaming Responses][0]
-
- [0]: /quickstart#streaming-responses
- """
- request = self.build_request(
- method=method,
- url=url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- )
- response = self.send(
- request=request,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- stream=True,
- )
- try:
- yield response
- finally:
- response.close()
-
- def send(
- self,
- request: Request,
- *,
- stream: bool = False,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a request.
-
- The request is sent as-is, unmodified.
-
- Typically you'll want to build one with `Client.build_request()`
- so that any client-level configuration is merged into the request,
- but passing an explicit `httpx.Request()` is supported as well.
-
- See also: [Request instances][0]
-
- [0]: /advanced/#request-instances
- """
- if self._state == ClientState.CLOSED:
- raise RuntimeError("Cannot send a request, as the client has been closed.")
-
- self._state = ClientState.OPENED
- timeout = (
- self.timeout if isinstance(timeout, UseClientDefault) else Timeout(timeout)
- )
- allow_redirects = (
- self.allow_redirects
- if isinstance(allow_redirects, UseClientDefault)
- else allow_redirects
- )
-
- auth = self._build_request_auth(request, auth)
-
- response = self._send_handling_auth(
- request,
- auth=auth,
- timeout=timeout,
- allow_redirects=allow_redirects,
- history=[],
- )
- try:
- if not stream:
- response.read()
-
- return response
-
- except Exception as exc:
- response.close()
- raise exc
-
- def _send_handling_auth(
- self,
- request: Request,
- auth: Auth,
- timeout: Timeout,
- allow_redirects: bool,
- history: typing.List[Response],
- ) -> Response:
- auth_flow = auth.sync_auth_flow(request)
- try:
- request = next(auth_flow)
-
- while True:
- response = self._send_handling_redirects(
- request,
- timeout=timeout,
- allow_redirects=allow_redirects,
- history=history,
- )
- try:
- try:
- next_request = auth_flow.send(response)
- except StopIteration:
- return response
-
- response.history = list(history)
- response.read()
- request = next_request
- history.append(response)
-
- except Exception as exc:
- response.close()
- raise exc
- finally:
- auth_flow.close()
-
- def _send_handling_redirects(
- self,
- request: Request,
- timeout: Timeout,
- allow_redirects: bool,
- history: typing.List[Response],
- ) -> Response:
- while True:
- if len(history) > self.max_redirects:
- raise TooManyRedirects(
- "Exceeded maximum allowed redirects.", request=request
- )
-
- for hook in self._event_hooks["request"]:
- hook(request)
-
- response = self._send_single_request(request, timeout)
- try:
- for hook in self._event_hooks["response"]:
- hook(response)
- response.history = list(history)
-
- if not response.is_redirect:
- return response
-
- request = self._build_redirect_request(request, response)
- history = history + [response]
-
- if allow_redirects:
- response.read()
- else:
- response.next_request = request
- return response
-
- except Exception as exc:
- response.close()
- raise exc
-
- def _send_single_request(self, request: Request, timeout: Timeout) -> Response:
- """
- Sends a single request, without handling any redirections.
- """
- transport = self._transport_for_url(request.url)
- timer = Timer()
- timer.sync_start()
-
- if not isinstance(request.stream, SyncByteStream):
- raise RuntimeError(
- "Attempted to send an async request with a sync Client instance."
- )
-
- with request_context(request=request):
- (status_code, headers, stream, extensions) = transport.handle_request(
- request.method.encode(),
- request.url.raw,
- headers=request.headers.raw,
- stream=request.stream,
- extensions={"timeout": timeout.as_dict()},
- )
-
- response = Response(
- status_code,
- headers=headers,
- stream=stream,
- extensions=extensions,
- request=request,
- )
-
- response.stream = BoundSyncStream(stream, response=response, timer=timer)
- self.cookies.extract_cookies(response)
-
- status = f"{response.status_code} {response.reason_phrase}"
- response_line = f"{response.http_version} {status}"
- logger.debug(f'HTTP Request: {request.method} {request.url} "{response_line}"')
-
- return response
-
- def get(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a `GET` request.
-
- **Parameters**: See `httpx.request`.
- """
- return self.request(
- "GET",
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- def options(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send an `OPTIONS` request.
-
- **Parameters**: See `httpx.request`.
- """
- return self.request(
- "OPTIONS",
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- def head(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a `HEAD` request.
-
- **Parameters**: See `httpx.request`.
- """
- return self.request(
- "HEAD",
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- def post(
- self,
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a `POST` request.
-
- **Parameters**: See `httpx.request`.
- """
- return self.request(
- "POST",
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- def put(
- self,
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a `PUT` request.
-
- **Parameters**: See `httpx.request`.
- """
- return self.request(
- "PUT",
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- def patch(
- self,
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a `PATCH` request.
-
- **Parameters**: See `httpx.request`.
- """
- return self.request(
- "PATCH",
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- def delete(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a `DELETE` request.
-
- **Parameters**: See `httpx.request`.
- """
- return self.request(
- "DELETE",
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- def close(self) -> None:
- """
- Close transport and proxies.
- """
- if self._state != ClientState.CLOSED:
- self._state = ClientState.CLOSED
-
- self._transport.close()
- for transport in self._mounts.values():
- if transport is not None:
- transport.close()
-
- def __enter__(self: T) -> T:
- if self._state != ClientState.UNOPENED:
- msg = {
- ClientState.OPENED: "Cannot open a client instance more than once.",
- ClientState.CLOSED: "Cannot reopen a client instance, once it has been closed.",
- }[self._state]
- raise RuntimeError(msg)
-
- self._state = ClientState.OPENED
-
- self._transport.__enter__()
- for transport in self._mounts.values():
- if transport is not None:
- transport.__enter__()
- return self
-
- def __exit__(
- self,
- exc_type: typing.Type[BaseException] = None,
- exc_value: BaseException = None,
- traceback: TracebackType = None,
- ) -> None:
- self._state = ClientState.CLOSED
-
- self._transport.__exit__(exc_type, exc_value, traceback)
- for transport in self._mounts.values():
- if transport is not None:
- transport.__exit__(exc_type, exc_value, traceback)
-
- def __del__(self) -> None:
- # We use 'getattr' here, to manage the case where '__del__()' is called
- # on a partically initiallized instance that raised an exception during
- # the call to '__init__()'.
- if getattr(self, "_state", None) == ClientState.OPENED: # noqa: B009
- self.close()
-
-
-class AsyncClient(BaseClient):
- """
- An asynchronous HTTP client, with connection pooling, HTTP/2, redirects,
- cookie persistence, etc.
-
- Usage:
-
- ```python
- >>> async with httpx.AsyncClient() as client:
- >>> response = await client.get('https://example.org')
- ```
-
- **Parameters:**
-
- * **auth** - *(optional)* An authentication class to use when sending
- requests.
- * **params** - *(optional)* Query parameters to include in request URLs, as
- a string, dictionary, or sequence of two-tuples.
- * **headers** - *(optional)* Dictionary of HTTP headers to include when
- sending requests.
- * **cookies** - *(optional)* Dictionary of Cookie items to include when
- sending requests.
- * **verify** - *(optional)* SSL certificates (a.k.a CA bundle) used to
- verify the identity of requested hosts. Either `True` (default CA bundle),
- a path to an SSL certificate file, or `False` (disable verification).
- * **cert** - *(optional)* An SSL certificate used by the requested host
- to authenticate the client. Either a path to an SSL certificate file, or
- two-tuple of (certificate file, key file), or a three-tuple of (certificate
- file, key file, password).
- * **http2** - *(optional)* A boolean indicating if HTTP/2 support should be
- enabled. Defaults to `False`.
- * **proxies** - *(optional)* A dictionary mapping HTTP protocols to proxy
- URLs.
- * **timeout** - *(optional)* The timeout configuration to use when sending
- requests.
- * **limits** - *(optional)* The limits configuration to use.
- * **max_redirects** - *(optional)* The maximum number of redirect responses
- that should be followed.
- * **base_url** - *(optional)* A URL to use as the base when building
- request URLs.
- * **transport** - *(optional)* A transport class to use for sending requests
- over the network.
- * **app** - *(optional)* An ASGI application to send requests to,
- rather than sending actual network requests.
- * **trust_env** - *(optional)* Enables or disables usage of environment
- variables for configuration.
- """
-
- def __init__(
- self,
- *,
- auth: AuthTypes = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- verify: VerifyTypes = True,
- cert: CertTypes = None,
- http1: bool = True,
- http2: bool = False,
- proxies: ProxiesTypes = None,
- mounts: typing.Mapping[str, AsyncBaseTransport] = None,
- timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
- limits: Limits = DEFAULT_LIMITS,
- max_redirects: int = DEFAULT_MAX_REDIRECTS,
- event_hooks: typing.Mapping[str, typing.List[typing.Callable]] = None,
- base_url: URLTypes = "",
- transport: AsyncBaseTransport = None,
- app: typing.Callable = None,
- trust_env: bool = True,
- ):
- super().__init__(
- auth=auth,
- params=params,
- headers=headers,
- cookies=cookies,
- timeout=timeout,
- max_redirects=max_redirects,
- event_hooks=event_hooks,
- base_url=base_url,
- trust_env=trust_env,
- )
-
- if http2:
- try:
- import h2 # noqa
- except ImportError: # pragma: nocover
- raise ImportError(
- "Using http2=True, but the 'h2' package is not installed. "
- "Make sure to install httpx using `pip install httpx[http2]`."
- ) from None
-
- allow_env_proxies = trust_env and app is None and transport is None
- proxy_map = self._get_proxy_map(proxies, allow_env_proxies)
-
- self._transport = self._init_transport(
- verify=verify,
- cert=cert,
- http1=http1,
- http2=http2,
- limits=limits,
- transport=transport,
- app=app,
- trust_env=trust_env,
- )
-
- self._mounts: typing.Dict[URLPattern, typing.Optional[AsyncBaseTransport]] = {
- URLPattern(key): None
- if proxy is None
- else self._init_proxy_transport(
- proxy,
- verify=verify,
- cert=cert,
- http1=http1,
- http2=http2,
- limits=limits,
- trust_env=trust_env,
- )
- for key, proxy in proxy_map.items()
- }
- if mounts is not None:
- self._mounts.update(
- {URLPattern(key): transport for key, transport in mounts.items()}
- )
- self._mounts = dict(sorted(self._mounts.items()))
-
- def _init_transport(
- self,
- verify: VerifyTypes = True,
- cert: CertTypes = None,
- http1: bool = True,
- http2: bool = False,
- limits: Limits = DEFAULT_LIMITS,
- transport: AsyncBaseTransport = None,
- app: typing.Callable = None,
- trust_env: bool = True,
- ) -> AsyncBaseTransport:
- if transport is not None:
- return transport
-
- if app is not None:
- return ASGITransport(app=app)
-
- return AsyncHTTPTransport(
- verify=verify,
- cert=cert,
- http1=http1,
- http2=http2,
- limits=limits,
- trust_env=trust_env,
- )
-
- def _init_proxy_transport(
- self,
- proxy: Proxy,
- verify: VerifyTypes = True,
- cert: CertTypes = None,
- http1: bool = True,
- http2: bool = False,
- limits: Limits = DEFAULT_LIMITS,
- trust_env: bool = True,
- ) -> AsyncBaseTransport:
- return AsyncHTTPTransport(
- verify=verify,
- cert=cert,
- http2=http2,
- limits=limits,
- trust_env=trust_env,
- proxy=proxy,
- )
-
- def _transport_for_url(self, url: URL) -> AsyncBaseTransport:
- """
- Returns the transport instance that should be used for a given URL.
- This will either be the standard connection pool, or a proxy.
- """
- for pattern, transport in self._mounts.items():
- if pattern.matches(url):
- return self._transport if transport is None else transport
-
- return self._transport
-
- async def request(
- self,
- method: str,
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Build and send a request.
-
- Equivalent to:
-
- ```python
- request = client.build_request(...)
- response = await client.send(request, ...)
- ```
-
- See `AsyncClient.build_request()`, `AsyncClient.send()`
- and [Merging of configuration][0] for how the various parameters
- are merged with client-level configuration.
-
- [0]: /advanced/#merging-of-configuration
- """
- request = self.build_request(
- method=method,
- url=url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- )
- response = await self.send(
- request, auth=auth, allow_redirects=allow_redirects, timeout=timeout
- )
- return response
-
- @asynccontextmanager
- async def stream(
- self,
- method: str,
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> typing.AsyncIterator[Response]:
- """
- Alternative to `httpx.request()` that streams the response body
- instead of loading it into memory at once.
-
- **Parameters**: See `httpx.request`.
-
- See also: [Streaming Responses][0]
-
- [0]: /quickstart#streaming-responses
- """
- request = self.build_request(
- method=method,
- url=url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- )
- response = await self.send(
- request=request,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- stream=True,
- )
- try:
- yield response
- finally:
- await response.aclose()
-
- async def send(
- self,
- request: Request,
- *,
- stream: bool = False,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a request.
-
- The request is sent as-is, unmodified.
-
- Typically you'll want to build one with `AsyncClient.build_request()`
- so that any client-level configuration is merged into the request,
- but passing an explicit `httpx.Request()` is supported as well.
-
- See also: [Request instances][0]
-
- [0]: /advanced/#request-instances
- """
- if self._state == ClientState.CLOSED:
- raise RuntimeError("Cannot send a request, as the client has been closed.")
-
- self._state = ClientState.OPENED
- timeout = (
- self.timeout if isinstance(timeout, UseClientDefault) else Timeout(timeout)
- )
- allow_redirects = (
- self.allow_redirects
- if isinstance(allow_redirects, UseClientDefault)
- else allow_redirects
- )
-
- auth = self._build_request_auth(request, auth)
-
- response = await self._send_handling_auth(
- request,
- auth=auth,
- timeout=timeout,
- allow_redirects=allow_redirects,
- history=[],
- )
- try:
- if not stream:
- await response.aread()
-
- return response
-
- except Exception as exc: # pragma: no cover
- await response.aclose()
- raise exc
-
- async def _send_handling_auth(
- self,
- request: Request,
- auth: Auth,
- timeout: Timeout,
- allow_redirects: bool,
- history: typing.List[Response],
- ) -> Response:
- auth_flow = auth.async_auth_flow(request)
- try:
- request = await auth_flow.__anext__()
-
- while True:
- response = await self._send_handling_redirects(
- request,
- timeout=timeout,
- allow_redirects=allow_redirects,
- history=history,
- )
- try:
- try:
- next_request = await auth_flow.asend(response)
- except StopAsyncIteration:
- return response
-
- response.history = list(history)
- await response.aread()
- request = next_request
- history.append(response)
-
- except Exception as exc:
- await response.aclose()
- raise exc
- finally:
- await auth_flow.aclose()
-
- async def _send_handling_redirects(
- self,
- request: Request,
- timeout: Timeout,
- allow_redirects: bool,
- history: typing.List[Response],
- ) -> Response:
- while True:
- if len(history) > self.max_redirects:
- raise TooManyRedirects(
- "Exceeded maximum allowed redirects.", request=request
- )
-
- for hook in self._event_hooks["request"]:
- await hook(request)
-
- response = await self._send_single_request(request, timeout)
- try:
- for hook in self._event_hooks["response"]:
- await hook(response)
-
- response.history = list(history)
-
- if not response.is_redirect:
- return response
-
- request = self._build_redirect_request(request, response)
- history = history + [response]
-
- if allow_redirects:
- await response.aread()
- else:
- response.next_request = request
- return response
-
- except Exception as exc:
- await response.aclose()
- raise exc
-
- async def _send_single_request(
- self, request: Request, timeout: Timeout
- ) -> Response:
- """
- Sends a single request, without handling any redirections.
- """
- transport = self._transport_for_url(request.url)
- timer = Timer()
- await timer.async_start()
-
- if not isinstance(request.stream, AsyncByteStream):
- raise RuntimeError(
- "Attempted to send an sync request with an AsyncClient instance."
- )
-
- with request_context(request=request):
- (
- status_code,
- headers,
- stream,
- extensions,
- ) = await transport.handle_async_request(
- request.method.encode(),
- request.url.raw,
- headers=request.headers.raw,
- stream=request.stream,
- extensions={"timeout": timeout.as_dict()},
- )
-
- response = Response(
- status_code,
- headers=headers,
- stream=stream,
- extensions=extensions,
- request=request,
- )
-
- response.stream = BoundAsyncStream(stream, response=response, timer=timer)
- self.cookies.extract_cookies(response)
-
- status = f"{response.status_code} {response.reason_phrase}"
- response_line = f"{response.http_version} {status}"
- logger.debug(f'HTTP Request: {request.method} {request.url} "{response_line}"')
-
- return response
-
- async def get(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a `GET` request.
-
- **Parameters**: See `httpx.request`.
- """
- return await self.request(
- "GET",
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- async def options(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send an `OPTIONS` request.
-
- **Parameters**: See `httpx.request`.
- """
- return await self.request(
- "OPTIONS",
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- async def head(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a `HEAD` request.
-
- **Parameters**: See `httpx.request`.
- """
- return await self.request(
- "HEAD",
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- async def post(
- self,
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a `POST` request.
-
- **Parameters**: See `httpx.request`.
- """
- return await self.request(
- "POST",
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- async def put(
- self,
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a `PUT` request.
-
- **Parameters**: See `httpx.request`.
- """
- return await self.request(
- "PUT",
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- async def patch(
- self,
- url: URLTypes,
- *,
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: typing.Any = None,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a `PATCH` request.
-
- **Parameters**: See `httpx.request`.
- """
- return await self.request(
- "PATCH",
- url,
- content=content,
- data=data,
- files=files,
- json=json,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- async def delete(
- self,
- url: URLTypes,
- *,
- params: QueryParamTypes = None,
- headers: HeaderTypes = None,
- cookies: CookieTypes = None,
- auth: typing.Union[AuthTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- allow_redirects: typing.Union[bool, UseClientDefault] = USE_CLIENT_DEFAULT,
- timeout: typing.Union[TimeoutTypes, UseClientDefault] = USE_CLIENT_DEFAULT,
- ) -> Response:
- """
- Send a `DELETE` request.
-
- **Parameters**: See `httpx.request`.
- """
- return await self.request(
- "DELETE",
- url,
- params=params,
- headers=headers,
- cookies=cookies,
- auth=auth,
- allow_redirects=allow_redirects,
- timeout=timeout,
- )
-
- async def aclose(self) -> None:
- """
- Close transport and proxies.
- """
- if self._state != ClientState.CLOSED:
- self._state = ClientState.CLOSED
-
- await self._transport.aclose()
- for proxy in self._mounts.values():
- if proxy is not None:
- await proxy.aclose()
-
- async def __aenter__(self: U) -> U:
- if self._state != ClientState.UNOPENED:
- msg = {
- ClientState.OPENED: "Cannot open a client instance more than once.",
- ClientState.CLOSED: "Cannot reopen a client instance, once it has been closed.",
- }[self._state]
- raise RuntimeError(msg)
-
- self._state = ClientState.OPENED
-
- await self._transport.__aenter__()
- for proxy in self._mounts.values():
- if proxy is not None:
- await proxy.__aenter__()
- return self
-
- async def __aexit__(
- self,
- exc_type: typing.Type[BaseException] = None,
- exc_value: BaseException = None,
- traceback: TracebackType = None,
- ) -> None:
- self._state = ClientState.CLOSED
-
- await self._transport.__aexit__(exc_type, exc_value, traceback)
- for proxy in self._mounts.values():
- if proxy is not None:
- await proxy.__aexit__(exc_type, exc_value, traceback)
-
- def __del__(self) -> None:
- # We use 'getattr' here, to manage the case where '__del__()' is called
- # on a partically initiallized instance that raised an exception during
- # the call to '__init__()'.
- if getattr(self, "_state", None) == ClientState.OPENED: # noqa: B009
- # Unlike the sync case, we cannot silently close the client when
- # it is garbage collected, because `.aclose()` is an async operation,
- # but `__del__` is not.
- #
- # For this reason we require explicit close management for
- # `AsyncClient`, and issue a warning on unclosed clients.
- #
- # The context managed style is usually preferable, because it neatly
- # ensures proper resource cleanup:
- #
- # async with httpx.AsyncClient() as client:
- # ...
- #
- # However, an explicit call to `aclose()` is also sufficient:
- #
- # client = httpx.AsyncClient()
- # try:
- # ...
- # finally:
- # await client.aclose()
- warnings.warn(
- f"Unclosed {self!r}. "
- "See https://www.python-httpx.org/async/#opening-and-closing-clients "
- "for details."
- )
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/_compat.py b/IKEA_scraper/.venv/Lib/site-packages/httpx/_compat.py
deleted file mode 100644
index 15e915a9..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx/_compat.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""
-The _compat module is used for code which requires branching between different
-Python environments. It is excluded from the code coverage checks.
-"""
-import ssl
-import sys
-
-# `contextlib.asynccontextmanager` exists from Python 3.7 onwards.
-# For 3.6 we require the `async_generator` package for a backported version.
-try:
- from contextlib import asynccontextmanager # type: ignore
-except ImportError:
- from async_generator import asynccontextmanager # type: ignore # noqa
-
-# Brotli support is optional
-# The C bindings in `brotli` are recommended for CPython.
-# The CFFI bindings in `brotlicffi` are recommended for PyPy and everything else.
-try:
- import brotlicffi as brotli
-except ImportError: # pragma: nocover
- try:
- import brotli
- except ImportError:
- brotli = None
-
-if sys.version_info >= (3, 10) or (
- sys.version_info >= (3, 7) and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0, 7)
-):
-
- def set_minimum_tls_version_1_2(context: ssl.SSLContext) -> None:
- # The OP_NO_SSL* and OP_NO_TLS* become deprecated in favor of
- # 'SSLContext.minimum_version' from Python 3.7 onwards, however
- # this attribute is not available unless the ssl module is compiled
- # with OpenSSL 1.1.0g or newer.
- # https://docs.python.org/3.10/library/ssl.html#ssl.SSLContext.minimum_version
- # https://docs.python.org/3.7/library/ssl.html#ssl.SSLContext.minimum_version
- context.minimum_version = ssl.TLSVersion.TLSv1_2
-
-
-else:
-
- def set_minimum_tls_version_1_2(context: ssl.SSLContext) -> None:
- # If 'minimum_version' isn't available, we configure these options with
- # the older deprecated variants.
- context.options |= ssl.OP_NO_SSLv2
- context.options |= ssl.OP_NO_SSLv3
- context.options |= ssl.OP_NO_TLSv1
- context.options |= ssl.OP_NO_TLSv1_1
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/_config.py b/IKEA_scraper/.venv/Lib/site-packages/httpx/_config.py
deleted file mode 100644
index 927a67c2..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx/_config.py
+++ /dev/null
@@ -1,349 +0,0 @@
-import os
-import ssl
-import typing
-from base64 import b64encode
-from pathlib import Path
-
-import certifi
-
-from ._compat import set_minimum_tls_version_1_2
-from ._models import URL, Headers
-from ._types import CertTypes, HeaderTypes, TimeoutTypes, URLTypes, VerifyTypes
-from ._utils import get_ca_bundle_from_env, get_logger
-
-DEFAULT_CIPHERS = ":".join(
- [
- "ECDHE+AESGCM",
- "ECDHE+CHACHA20",
- "DHE+AESGCM",
- "DHE+CHACHA20",
- "ECDH+AESGCM",
- "DH+AESGCM",
- "ECDH+AES",
- "DH+AES",
- "RSA+AESGCM",
- "RSA+AES",
- "!aNULL",
- "!eNULL",
- "!MD5",
- "!DSS",
- ]
-)
-
-
-logger = get_logger(__name__)
-
-
-class UnsetType:
- pass # pragma: nocover
-
-
-UNSET = UnsetType()
-
-
-def create_ssl_context(
- cert: CertTypes = None,
- verify: VerifyTypes = True,
- trust_env: bool = True,
- http2: bool = False,
-) -> ssl.SSLContext:
- return SSLConfig(
- cert=cert, verify=verify, trust_env=trust_env, http2=http2
- ).ssl_context
-
-
-class SSLConfig:
- """
- SSL Configuration.
- """
-
- DEFAULT_CA_BUNDLE_PATH = Path(certifi.where())
-
- def __init__(
- self,
- *,
- cert: CertTypes = None,
- verify: VerifyTypes = True,
- trust_env: bool = True,
- http2: bool = False,
- ):
- self.cert = cert
- self.verify = verify
- self.trust_env = trust_env
- self.http2 = http2
- self.ssl_context = self.load_ssl_context()
-
- def load_ssl_context(self) -> ssl.SSLContext:
- logger.trace(
- f"load_ssl_context "
- f"verify={self.verify!r} "
- f"cert={self.cert!r} "
- f"trust_env={self.trust_env!r} "
- f"http2={self.http2!r}"
- )
-
- if self.verify:
- return self.load_ssl_context_verify()
- return self.load_ssl_context_no_verify()
-
- def load_ssl_context_no_verify(self) -> ssl.SSLContext:
- """
- Return an SSL context for unverified connections.
- """
- context = self._create_default_ssl_context()
- context.check_hostname = False
- context.verify_mode = ssl.CERT_NONE
- self._load_client_certs(context)
- return context
-
- def load_ssl_context_verify(self) -> ssl.SSLContext:
- """
- Return an SSL context for verified connections.
- """
- if self.trust_env and self.verify is True:
- ca_bundle = get_ca_bundle_from_env()
- if ca_bundle is not None:
- self.verify = ca_bundle
-
- if isinstance(self.verify, ssl.SSLContext):
- # Allow passing in our own SSLContext object that's pre-configured.
- context = self.verify
- self._load_client_certs(context)
- return context
- elif isinstance(self.verify, bool):
- ca_bundle_path = self.DEFAULT_CA_BUNDLE_PATH
- elif Path(self.verify).exists():
- ca_bundle_path = Path(self.verify)
- else:
- raise IOError(
- "Could not find a suitable TLS CA certificate bundle, "
- "invalid path: {}".format(self.verify)
- )
-
- context = self._create_default_ssl_context()
- context.verify_mode = ssl.CERT_REQUIRED
- context.check_hostname = True
-
- # Signal to server support for PHA in TLS 1.3. Raises an
- # AttributeError if only read-only access is implemented.
- try:
- context.post_handshake_auth = True # type: ignore
- except AttributeError: # pragma: nocover
- pass
-
- # Disable using 'commonName' for SSLContext.check_hostname
- # when the 'subjectAltName' extension isn't available.
- try:
- context.hostname_checks_common_name = False # type: ignore
- except AttributeError: # pragma: nocover
- pass
-
- if ca_bundle_path.is_file():
- logger.trace(f"load_verify_locations cafile={ca_bundle_path!s}")
- context.load_verify_locations(cafile=str(ca_bundle_path))
- elif ca_bundle_path.is_dir():
- logger.trace(f"load_verify_locations capath={ca_bundle_path!s}")
- context.load_verify_locations(capath=str(ca_bundle_path))
-
- self._load_client_certs(context)
-
- return context
-
- def _create_default_ssl_context(self) -> ssl.SSLContext:
- """
- Creates the default SSLContext object that's used for both verified
- and unverified connections.
- """
- context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
- set_minimum_tls_version_1_2(context)
- context.options |= ssl.OP_NO_COMPRESSION
- context.set_ciphers(DEFAULT_CIPHERS)
-
- if ssl.HAS_ALPN:
- alpn_idents = ["http/1.1", "h2"] if self.http2 else ["http/1.1"]
- context.set_alpn_protocols(alpn_idents)
-
- if hasattr(context, "keylog_filename"): # pragma: nocover (Available in 3.8+)
- keylogfile = os.environ.get("SSLKEYLOGFILE")
- if keylogfile and self.trust_env:
- context.keylog_filename = keylogfile # type: ignore
-
- return context
-
- def _load_client_certs(self, ssl_context: ssl.SSLContext) -> None:
- """
- Loads client certificates into our SSLContext object
- """
- if self.cert is not None:
- if isinstance(self.cert, str):
- ssl_context.load_cert_chain(certfile=self.cert)
- elif isinstance(self.cert, tuple) and len(self.cert) == 2:
- ssl_context.load_cert_chain(certfile=self.cert[0], keyfile=self.cert[1])
- elif isinstance(self.cert, tuple) and len(self.cert) == 3:
- ssl_context.load_cert_chain(
- certfile=self.cert[0],
- keyfile=self.cert[1],
- password=self.cert[2], # type: ignore
- )
-
-
-class Timeout:
- """
- Timeout configuration.
-
- **Usage**:
-
- Timeout(None) # No timeouts.
- Timeout(5.0) # 5s timeout on all operations.
- Timeout(None, connect=5.0) # 5s timeout on connect, no other timeouts.
- Timeout(5.0, connect=10.0) # 10s timeout on connect. 5s timeout elsewhere.
- Timeout(5.0, pool=None) # No timeout on acquiring connection from pool.
- # 5s timeout elsewhere.
- """
-
- def __init__(
- self,
- timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
- *,
- connect: typing.Union[None, float, UnsetType] = UNSET,
- read: typing.Union[None, float, UnsetType] = UNSET,
- write: typing.Union[None, float, UnsetType] = UNSET,
- pool: typing.Union[None, float, UnsetType] = UNSET,
- ):
- if isinstance(timeout, Timeout):
- # Passed as a single explicit Timeout.
- assert connect is UNSET
- assert read is UNSET
- assert write is UNSET
- assert pool is UNSET
- self.connect = timeout.connect # type: typing.Optional[float]
- self.read = timeout.read # type: typing.Optional[float]
- self.write = timeout.write # type: typing.Optional[float]
- self.pool = timeout.pool # type: typing.Optional[float]
- elif isinstance(timeout, tuple):
- # Passed as a tuple.
- self.connect = timeout[0]
- self.read = timeout[1]
- self.write = None if len(timeout) < 3 else timeout[2]
- self.pool = None if len(timeout) < 4 else timeout[3]
- elif not (
- isinstance(connect, UnsetType)
- or isinstance(read, UnsetType)
- or isinstance(write, UnsetType)
- or isinstance(pool, UnsetType)
- ):
- self.connect = connect
- self.read = read
- self.write = write
- self.pool = pool
- else:
- if isinstance(timeout, UnsetType):
- raise ValueError(
- "httpx.Timeout must either include a default, or set all "
- "four parameters explicitly."
- )
- self.connect = timeout if isinstance(connect, UnsetType) else connect
- self.read = timeout if isinstance(read, UnsetType) else read
- self.write = timeout if isinstance(write, UnsetType) else write
- self.pool = timeout if isinstance(pool, UnsetType) else pool
-
- def as_dict(self) -> typing.Dict[str, typing.Optional[float]]:
- return {
- "connect": self.connect,
- "read": self.read,
- "write": self.write,
- "pool": self.pool,
- }
-
- def __eq__(self, other: typing.Any) -> bool:
- return (
- isinstance(other, self.__class__)
- and self.connect == other.connect
- and self.read == other.read
- and self.write == other.write
- and self.pool == other.pool
- )
-
- def __repr__(self) -> str:
- class_name = self.__class__.__name__
- if len({self.connect, self.read, self.write, self.pool}) == 1:
- return f"{class_name}(timeout={self.connect})"
- return (
- f"{class_name}(connect={self.connect}, "
- f"read={self.read}, write={self.write}, pool={self.pool})"
- )
-
-
-class Limits:
- """
- Configuration for limits to various client behaviors.
-
- **Parameters:**
-
- * **max_connections** - The maximum number of concurrent connections that may be
- established.
- * **max_keepalive_connections** - Allow the connection pool to maintain
- keep-alive connections below this point. Should be less than or equal
- to `max_connections`.
- """
-
- def __init__(
- self,
- *,
- max_connections: int = None,
- max_keepalive_connections: int = None,
- keepalive_expiry: typing.Optional[float] = 5.0,
- ):
- self.max_connections = max_connections
- self.max_keepalive_connections = max_keepalive_connections
- self.keepalive_expiry = keepalive_expiry
-
- def __eq__(self, other: typing.Any) -> bool:
- return (
- isinstance(other, self.__class__)
- and self.max_connections == other.max_connections
- and self.max_keepalive_connections == other.max_keepalive_connections
- and self.keepalive_expiry == other.keepalive_expiry
- )
-
- def __repr__(self) -> str:
- class_name = self.__class__.__name__
- return (
- f"{class_name}(max_connections={self.max_connections}, "
- f"max_keepalive_connections={self.max_keepalive_connections}, "
- f"keepalive_expiry={self.keepalive_expiry})"
- )
-
-
-class Proxy:
- def __init__(self, url: URLTypes, *, headers: HeaderTypes = None):
- url = URL(url)
- headers = Headers(headers)
-
- if url.scheme not in ("http", "https"):
- raise ValueError(f"Unknown scheme for proxy URL {url!r}")
-
- if url.username or url.password:
- headers.setdefault(
- "Proxy-Authorization",
- self._build_auth_header(url.username, url.password),
- )
- # Remove userinfo from the URL authority, e.g.:
- # 'username:password@proxy_host:proxy_port' -> 'proxy_host:proxy_port'
- url = url.copy_with(username=None, password=None)
-
- self.url = url
- self.headers = headers
-
- def _build_auth_header(self, username: str, password: str) -> str:
- userpass = (username.encode("utf-8"), password.encode("utf-8"))
- token = b64encode(b":".join(userpass)).decode()
- return f"Basic {token}"
-
- def __repr__(self) -> str:
- return f"Proxy(url={str(self.url)!r}, headers={dict(self.headers)!r})"
-
-
-DEFAULT_TIMEOUT_CONFIG = Timeout(timeout=5.0)
-DEFAULT_LIMITS = Limits(max_connections=100, max_keepalive_connections=20)
-DEFAULT_MAX_REDIRECTS = 20
diff --git a/IKEA_scraper/.venv/Lib/site-packages/httpx/_content.py b/IKEA_scraper/.venv/Lib/site-packages/httpx/_content.py
deleted file mode 100644
index 86f3c7c2..00000000
--- a/IKEA_scraper/.venv/Lib/site-packages/httpx/_content.py
+++ /dev/null
@@ -1,207 +0,0 @@
-import inspect
-import warnings
-from json import dumps as json_dumps
-from typing import (
- Any,
- AsyncIterable,
- AsyncIterator,
- Dict,
- Iterable,
- Iterator,
- Tuple,
- Union,
-)
-from urllib.parse import urlencode
-
-from ._exceptions import StreamClosed, StreamConsumed
-from ._multipart import MultipartStream
-from ._transports.base import AsyncByteStream, SyncByteStream
-from ._types import RequestContent, RequestData, RequestFiles, ResponseContent
-from ._utils import peek_filelike_length, primitive_value_to_str
-
-
-class ByteStream(AsyncByteStream, SyncByteStream):
- def __init__(self, stream: bytes) -> None:
- self._stream = stream
-
- def __iter__(self) -> Iterator[bytes]:
- yield self._stream
-
- async def __aiter__(self) -> AsyncIterator[bytes]:
- yield self._stream
-
-
-class IteratorByteStream(SyncByteStream):
- def __init__(self, stream: Iterable[bytes]):
- self._stream = stream
- self._is_stream_consumed = False
- self._is_generator = inspect.isgenerator(stream)
-
- def __iter__(self) -> Iterator[bytes]:
- if self._is_stream_consumed and self._is_generator:
- raise StreamConsumed()
-
- self._is_stream_consumed = True
- for part in self._stream:
- yield part
-
-
-class AsyncIteratorByteStream(AsyncByteStream):
- def __init__(self, stream: AsyncIterable[bytes]):
- self._stream = stream
- self._is_stream_consumed = False
- self._is_generator = inspect.isasyncgen(stream)
-
- async def __aiter__(self) -> AsyncIterator[bytes]:
- if self._is_stream_consumed and self._is_generator:
- raise StreamConsumed()
-
- self._is_stream_consumed = True
- async for part in self._stream:
- yield part
-
-
-class UnattachedStream(AsyncByteStream, SyncByteStream):
- """
- If a request or response is serialized using pickle, then it is no longer
- attached to a stream for I/O purposes. Any stream operations should result
- in `httpx.StreamClosed`.
- """
-
- def __iter__(self) -> Iterator[bytes]:
- raise StreamClosed()
-
- async def __aiter__(self) -> AsyncIterator[bytes]:
- raise StreamClosed()
- yield b"" # pragma: nocover
-
-
-def encode_content(
- content: Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]]
-) -> Tuple[Dict[str, str], Union[SyncByteStream, AsyncByteStream]]:
-
- if isinstance(content, (bytes, str)):
- body = content.encode("utf-8") if isinstance(content, str) else content
- content_length = len(body)
- headers = {"Content-Length": str(content_length)} if body else {}
- return headers, ByteStream(body)
-
- elif isinstance(content, Iterable):
- content_length_or_none = peek_filelike_length(content)
-
- if content_length_or_none is None:
- headers = {"Transfer-Encoding": "chunked"}
- else:
- headers = {"Content-Length": str(content_length_or_none)}
- return headers, IteratorByteStream(content) # type: ignore
-
- elif isinstance(content, AsyncIterable):
- headers = {"Transfer-Encoding": "chunked"}
- return headers, AsyncIteratorByteStream(content)
-
- raise TypeError(f"Unexpected type for 'content', {type(content)!r}")
-
-
-def encode_urlencoded_data(
- data: dict,
-) -> Tuple[Dict[str, str], ByteStream]:
- plain_data = []
- for key, value in data.items():
- if isinstance(value, (list, tuple)):
- plain_data.extend([(key, primitive_value_to_str(item)) for item in value])
- else:
- plain_data.append((key, primitive_value_to_str(value)))
- body = urlencode(plain_data, doseq=True).encode("utf-8")
- content_length = str(len(body))
- content_type = "application/x-www-form-urlencoded"
- headers = {"Content-Length": content_length, "Content-Type": content_type}
- return headers, ByteStream(body)
-
-
-def encode_multipart_data(
- data: dict, files: RequestFiles, boundary: bytes = None
-) -> Tuple[Dict[str, str], MultipartStream]:
- multipart = MultipartStream(data=data, files=files, boundary=boundary)
- headers = multipart.get_headers()
- return headers, multipart
-
-
-def encode_text(text: str) -> Tuple[Dict[str, str], ByteStream]:
- body = text.encode("utf-8")
- content_length = str(len(body))
- content_type = "text/plain; charset=utf-8"
- headers = {"Content-Length": content_length, "Content-Type": content_type}
- return headers, ByteStream(body)
-
-
-def encode_html(html: str) -> Tuple[Dict[str, str], ByteStream]:
- body = html.encode("utf-8")
- content_length = str(len(body))
- content_type = "text/html; charset=utf-8"
- headers = {"Content-Length": content_length, "Content-Type": content_type}
- return headers, ByteStream(body)
-
-
-def encode_json(json: Any) -> Tuple[Dict[str, str], ByteStream]:
- body = json_dumps(json).encode("utf-8")
- content_length = str(len(body))
- content_type = "application/json"
- headers = {"Content-Length": content_length, "Content-Type": content_type}
- return headers, ByteStream(body)
-
-
-def encode_request(
- content: RequestContent = None,
- data: RequestData = None,
- files: RequestFiles = None,
- json: Any = None,
- boundary: bytes = None,
-) -> Tuple[Dict[str, str], Union[SyncByteStream, AsyncByteStream]]:
- """
- Handles encoding the given `content`, `data`, `files`, and `json`,
- returning a two-tuple of (, ).
- """
- if data is not None and not isinstance(data, dict):
- # We prefer to seperate `content=`
- # for raw request content, and `data=