diff --git a/markdown/TEST-REPORT.md b/markdown/TEST-REPORT.md index e16329f16..6db623ca4 100644 --- a/markdown/TEST-REPORT.md +++ b/markdown/TEST-REPORT.md @@ -1,8 +1,8 @@ # 🧪 Test Report -*Generated on 2025-08-14 16:32:41 CEST* +*Generated on 2025-08-14 16:34:34 CEST* ## 🧾 General Info -- **duration**: 5.246391773223877 +- **duration**: 6.748669385910034 - **root**: /workspace/tligui_y/slic - **environment**: {} @@ -28,7 +28,7 @@ **duration:** ```python - 0.00035812100395560265 + 0.00038035912439227104 ``` **outcome:** @@ -42,7 +42,7 @@ **duration:** ```python - 0.011955311987549067 + 0.005957710091024637 ``` **outcome:** @@ -56,7 +56,8 @@ ```python path: /workspace/tligui_y/slic/tests/test_utils_elog.py lineno: 24 - message: Failed: elog.open() raised an unexpected exception: Invalid username or password. + message: Failed: elog.open() raised an unexpected exception: No response from the logbook server. + Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) ``` **traceback:** @@ -70,7 +71,1472 @@ **longrepr:** ```python - def test_get_default_elog_instance_with_direct_password_and_real_check(): + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + > sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection + raise err + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + address = ('localhost', 8080), timeout = None, source_address = None + socket_options = [(6, 1, 1)] + + def create_connection( + address: tuple[str, int], + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + socket_options: _TYPE_SOCKET_OPTIONS | None = None, + ) -> socket.socket: + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`socket.getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith("["): + host = host.strip("[]") + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + try: + host.encode("idna") + except UnicodeError: + raise LocationParseError(f"'{host}', label empty or too long") from None + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not _DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + > sock.connect(sa) + E ConnectionRefusedError: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError + + The above exception was the direct cause of the following exception: + + self = + method = 'POST', url = '/demo/' + body = b'--076f6dcb4da3b3dc1982af7cab425820\r\nContent-Disposition: form-data; name="Author"\r\n\r\nrobot\r\n--076f6dcb4da3b3...Disposition: form-data; name="Text"; filename=""\r\n\r\nThis is a message1\r\n--076f6dcb4da3b3dc1982af7cab425820--\r\n' + headers = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '705', 'Content-Type': 'multipart/form-data; boundary=076f6dcb4da3b3dc1982af7cab425820'} + retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) + redirect = False, assert_same_host = False + timeout = Timeout(connect=None, read=None, total=None), pool_timeout = None + release_conn = False, chunked = False, body_pos = None, preload_content = False + decode_content = False, response_kw = {} + parsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/', query=None, fragment=None) + destination_scheme = None, conn = None, release_this_conn = True + http_tunnel_required = False, err = None, clean_exit = False + + def urlopen( # type: ignore[override] + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + redirect: bool = True, + assert_same_host: bool = True, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + pool_timeout: int | None = None, + release_conn: bool | None = None, + chunked: bool = False, + body_pos: _TYPE_BODY_POSITION | None = None, + preload_content: bool = True, + decode_content: bool = True, + **response_kw: typing.Any, + ) -> BaseHTTPResponse: + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method + such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When ``False``, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param bool preload_content: + If True, the response's body will be preloaded into memory. + + :param bool decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of ``preload_content`` + which defaults to ``True``. + + :param bool chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param int body_pos: + Position to seek to in file-like body in the event of a retry or + redirect. Typically this won't need to be set because urllib3 will + auto-populate the value when needed. + """ + parsed_url = parse_url(url) + destination_scheme = parsed_url.scheme + + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = preload_content + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + # Ensure that the URL we're connecting to is properly encoded + if url.startswith("/"): + url = to_str(_encode_target(url)) + else: + url = to_str(parsed_url.url) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] + release_this_conn = release_conn + + http_tunnel_required = connection_requires_http_tunnel( + self.proxy, self.proxy_config, destination_scheme + ) + + # Merge the proxy headers. Only done when not using HTTP CONNECT. We + # have to copy the headers dict so we can safely change it without those + # changes being reflected in anyone else's copy. + if not http_tunnel_required: + headers = headers.copy() # type: ignore[attr-defined] + headers.update(self.proxy_headers) # type: ignore[union-attr] + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + # Rewind body position, if needed. Record current position + # for future rewinds in the event of a redirect/retry. + body_pos = set_file_position(body, body_pos) + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] + + # Is this a closed/new connection that requires CONNECT tunnelling? + if self.proxy is not None and http_tunnel_required and conn.is_closed: + try: + self._prepare_proxy(conn) + except (BaseSSLError, OSError, SocketTimeout) as e: + self._raise_timeout( + err=e, url=self.proxy.url, timeout_value=conn.timeout + ) + raise + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Make the request on the HTTPConnection object + > response = self._make_request( + conn, + method, + url, + timeout=timeout_obj, + body=body, + headers=headers, + chunked=chunked, + retries=retries, + response_conn=response_conn, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request + conn.request( + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request + self.endheaders() + .pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + .pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output + self.send(msg) + .pixi/envs/default/lib/python3.8/http/client.py:951: in send + self.connect() + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect + self.sock = self._new_conn() + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + except socket.gaierror as e: + raise NameResolutionError(self.host, self, e) from e + except SocketTimeout as e: + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + + except OSError as e: + > raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + E urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError + + The above exception was the direct cause of the following exception: + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + > resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen + retries = retries.increment( + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = Retry(total=0, connect=None, read=False, redirect=None, status=None) + method = 'POST', url = '/demo/', response = None + error = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused') + _pool = + _stacktrace = + + def increment( + self, + method: str | None = None, + url: str | None = None, + response: BaseHTTPResponse | None = None, + error: Exception | None = None, + _pool: ConnectionPool | None = None, + _stacktrace: TracebackType | None = None, + ) -> Self: + """Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.BaseHTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + connect = self.connect + read = self.read + redirect = self.redirect + status_count = self.status + other = self.other + cause = "unknown" + status = None + redirect_location = None + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False or method is None or not self._is_method_retryable(method): + raise reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + + elif error: + # Other retry? + if other is not None: + other -= 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = "too many redirects" + response_redirect_location = response.get_redirect_location() + if response_redirect_location: + redirect_location = response_redirect_location + status = response.status + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and the given method is in the allowed_methods + cause = ResponseError.GENERIC_ERROR + if response and response.status: + if status_count is not None: + status_count -= 1 + cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) + status = response.status + + history = self.history + ( + RequestHistory(method, url, error, status, redirect_location), + ) + + new_retry = self.new( + total=total, + connect=connect, + read=read, + redirect=redirect, + status=status_count, + other=other, + history=history, + ) + + if new_retry.is_exhausted(): + reason = error or ResponseError(cause) + > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError + + During handling of the above exception, another exception occurred: + + self = + message = 'This is a message1', msg_id = None, reply = False + attributes = {'Author': 'robot', 'When': 1755182070, 'cmd': 'Submit', 'exp': 'demo', ...} + attachments = [], suppress_email_notification = False, encoding = None + timeout = None, kwargs = {'Author': 'robot'} + logbook_directory = 'elog_instance/logbooks/demo' + new_attachment_list = [('Text', ('', b'This is a message1'))] + objects_to_close = [] + attributes_to_edit = {'Author': b'robot', 'When': 1755182070, 'cmd': b'Submit', 'exp': b'demo', ...} + + def post(self, message, msg_id=None, reply=False, attributes=None, attachments=None, + suppress_email_notification=False, encoding=None, timeout=None, **kwargs): + """ + Posts message to the logbook. If msg_id is not specified new message will be created, otherwise existing + message will be edited, or a reply (if reply=True) to it will be created. This method returns the msg_id + of the newly created message. + """ + + logbook_directory = "elog_instance/logbooks/demo" + print(f"Checking the existence of the directory {logbook_directory}") + + # Check if the directory exists + if not os.path.exists(logbook_directory): + print(f"The directory {logbook_directory} does not exist.") + else: + print(f"The directory {logbook_directory} exists.") + + # Check write permissions + if os.access(logbook_directory, os.W_OK): + print(f"The directory {logbook_directory} has write permissions.") + else: + print(f"The directory {logbook_directory} does not have write permissions.") + + + print("STARTING POST") + # Ajout des impressions pour déboguer + print(f"Message to post: {message}") + print(f"msg_id: {msg_id}") + print(f"Attributes: {attributes}") + print(f"Attachments: {attachments}") + print(f"Encoding: {encoding}") + print(f"Timeout: {timeout}") + print(f"Additional kwargs: {kwargs}") + + attributes = attributes or {} + attributes = {**attributes, **kwargs} # kwargs as attributes with higher priority + print(f"Updated attributes: {attributes}") + + attachments = attachments or [] + print(f"Attachments list: {attachments}") + + if encoding is not None: + if encoding not in ['plain', 'HTML', 'ELCode']: + raise LogbookMessageRejected('Invalid message encoding. Valid options: plain, HTML, ELCode.') + attributes['Encoding'] = encoding + + if suppress_email_notification: + attributes["suppress"] = 1 + + # Prepare attachments + if attachments: + new_attachment_list, objects_to_close = self._prepare_attachments(attachments) + print(f"New attachments prepared: {new_attachment_list}") + else: + objects_to_close = [] + new_attachment_list = [] + + attributes_to_edit = dict() + + if msg_id: + print(f"Editing message with msg_id: {msg_id}") + if reply: + print(f"Replying to message with msg_id: {msg_id}") + self._check_if_message_on_server(msg_id) + attributes['reply_to'] = str(msg_id) + else: + print("Editing existing message.") + attributes['edit_id'] = str(msg_id) + attributes['skiplock'] = '1' + msg_to_edit, attributes_to_edit, existing_attachments_list = self.read(msg_id) + + # Merge new attributes + for attribute, data in attributes.items(): + if data is not None: + attributes_to_edit[attribute] = data + + print(f"Attributes after merging: {attributes_to_edit}") + + # Process existing attachments + i = 0 + existing_attachments_filename_list = [] + for attachment in existing_attachments_list: + attributes_to_edit[f'attachment{i}'] = os.path.basename(attachment) + existing_attachments_filename_list.append(os.path.basename(attachment)[14:]) + i += 1 + + print(f"Existing attachments: {existing_attachments_filename_list}") + + duplicate_attachment_list = [] + for new_attachment in new_attachment_list: + new_attachment_filename = new_attachment[1][0] + print(f"Checking new attachment: {new_attachment_filename}") + if new_attachment_filename in existing_attachments_filename_list: + # Same attachment exists on the server, compare content + new_attachment_content = new_attachment[1][1].read() + new_attachment[1][1].seek(0) + attachment_index = existing_attachments_filename_list.index(new_attachment_filename) + existing_attachment_content = self.download_attachment( + url=existing_attachments_list[attachment_index], + timeout=timeout + ) + if new_attachment_content == existing_attachment_content: + print(f"Duplicate attachment detected: {new_attachment_filename}") + duplicate_attachment_list.append(new_attachment) + else: + print(f"Attachment content has changed: {new_attachment_filename}") + self.delete_attachment(msg_id, attributes=attributes_to_edit, + attachment_id=attachment_index, + timeout=timeout, text=msg_to_edit) + existing_attachments_filename_list.pop(attachment_index) + existing_attachments_list.pop(attachment_index) + + print(f"Duplicate attachments to remove: {duplicate_attachment_list}") + + # Remove duplicates + for attach in duplicate_attachment_list: + new_attachment_list.remove(attach) + + print(f"Final new attachments list: {new_attachment_list}") + else: + # Creating a new message, add timestamp if not present + if 'When' not in attributes: + attributes['When'] = int(datetime.now().timestamp()) + + # Final check on attributes + if not attributes_to_edit: + attributes_to_edit = attributes + + print(f"Final attributes to send to the server: {attributes_to_edit}") + + # Remove reserved attributes + _remove_reserved_attributes(attributes_to_edit) + + new_attachment_list.append(('Text', ('', message.encode('iso-8859-1')))) + print(f"Final attachment list including message text: {new_attachment_list}") + + # Add base message attributes + self._add_base_msg_attributes(attributes_to_edit) + print(f"Attributes with base message added: {attributes_to_edit}") + + # Sanitize attribute keys + attributes_to_edit = _replace_special_characters_in_attribute_keys(attributes_to_edit) + print(f"Attributes after sanitizing keys: {attributes_to_edit}") + + # Encode all string values in latin1 + attributes_to_edit = _encode_values(attributes_to_edit) + print(f"Attributes after encoding: {attributes_to_edit}") + + try: + print("Sending POST request to the server...") + > response = requests.post(self._url, data=attributes_to_edit, files=new_attachment_list, + allow_redirects=False, verify=False, timeout=timeout) + + slic/utils/logbook.py:269: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:115: in post + return request("post", url, data=data, json=json, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request + return session.request(method=method, url=url, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request + resp = self.send(prep, **send_kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send + r = adapter.send(request, **kwargs) + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + except (ProtocolError, OSError) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + + > raise ConnectionError(e, request=request) + E requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError + + During handling of the above exception, another exception occurred: + + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + > sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection + raise err + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + address = ('localhost', 8080), timeout = None, source_address = None + socket_options = [(6, 1, 1)] + + def create_connection( + address: tuple[str, int], + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + socket_options: _TYPE_SOCKET_OPTIONS | None = None, + ) -> socket.socket: + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`socket.getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith("["): + host = host.strip("[]") + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + try: + host.encode("idna") + except UnicodeError: + raise LocationParseError(f"'{host}', label empty or too long") from None + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not _DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + > sock.connect(sa) + E ConnectionRefusedError: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError + + The above exception was the direct cause of the following exception: + + self = + method = 'GET', url = '/demo/None', body = None + headers = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'unm=robot;upwd=testpassword;'} + retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) + redirect = False, assert_same_host = False + timeout = Timeout(connect=None, read=None, total=None), pool_timeout = None + release_conn = False, chunked = False, body_pos = None, preload_content = False + decode_content = False, response_kw = {} + parsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/None', query=None, fragment=None) + destination_scheme = None, conn = None, release_this_conn = True + http_tunnel_required = False, err = None, clean_exit = False + + def urlopen( # type: ignore[override] + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + redirect: bool = True, + assert_same_host: bool = True, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + pool_timeout: int | None = None, + release_conn: bool | None = None, + chunked: bool = False, + body_pos: _TYPE_BODY_POSITION | None = None, + preload_content: bool = True, + decode_content: bool = True, + **response_kw: typing.Any, + ) -> BaseHTTPResponse: + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method + such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When ``False``, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param bool preload_content: + If True, the response's body will be preloaded into memory. + + :param bool decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of ``preload_content`` + which defaults to ``True``. + + :param bool chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param int body_pos: + Position to seek to in file-like body in the event of a retry or + redirect. Typically this won't need to be set because urllib3 will + auto-populate the value when needed. + """ + parsed_url = parse_url(url) + destination_scheme = parsed_url.scheme + + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = preload_content + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + # Ensure that the URL we're connecting to is properly encoded + if url.startswith("/"): + url = to_str(_encode_target(url)) + else: + url = to_str(parsed_url.url) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] + release_this_conn = release_conn + + http_tunnel_required = connection_requires_http_tunnel( + self.proxy, self.proxy_config, destination_scheme + ) + + # Merge the proxy headers. Only done when not using HTTP CONNECT. We + # have to copy the headers dict so we can safely change it without those + # changes being reflected in anyone else's copy. + if not http_tunnel_required: + headers = headers.copy() # type: ignore[attr-defined] + headers.update(self.proxy_headers) # type: ignore[union-attr] + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + # Rewind body position, if needed. Record current position + # for future rewinds in the event of a redirect/retry. + body_pos = set_file_position(body, body_pos) + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] + + # Is this a closed/new connection that requires CONNECT tunnelling? + if self.proxy is not None and http_tunnel_required and conn.is_closed: + try: + self._prepare_proxy(conn) + except (BaseSSLError, OSError, SocketTimeout) as e: + self._raise_timeout( + err=e, url=self.proxy.url, timeout_value=conn.timeout + ) + raise + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Make the request on the HTTPConnection object + > response = self._make_request( + conn, + method, + url, + timeout=timeout_obj, + body=body, + headers=headers, + chunked=chunked, + retries=retries, + response_conn=response_conn, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request + conn.request( + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request + self.endheaders() + .pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + .pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output + self.send(msg) + .pixi/envs/default/lib/python3.8/http/client.py:951: in send + self.connect() + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect + self.sock = self._new_conn() + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + except socket.gaierror as e: + raise NameResolutionError(self.host, self, e) from e + except SocketTimeout as e: + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + + except OSError as e: + > raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + E urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError + + The above exception was the direct cause of the following exception: + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + > resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen + retries = retries.increment( + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = Retry(total=0, connect=None, read=False, redirect=None, status=None) + method = 'GET', url = '/demo/None', response = None + error = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused') + _pool = + _stacktrace = + + def increment( + self, + method: str | None = None, + url: str | None = None, + response: BaseHTTPResponse | None = None, + error: Exception | None = None, + _pool: ConnectionPool | None = None, + _stacktrace: TracebackType | None = None, + ) -> Self: + """Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.BaseHTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + connect = self.connect + read = self.read + redirect = self.redirect + status_count = self.status + other = self.other + cause = "unknown" + status = None + redirect_location = None + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False or method is None or not self._is_method_retryable(method): + raise reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + + elif error: + # Other retry? + if other is not None: + other -= 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = "too many redirects" + response_redirect_location = response.get_redirect_location() + if response_redirect_location: + redirect_location = response_redirect_location + status = response.status + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and the given method is in the allowed_methods + cause = ResponseError.GENERIC_ERROR + if response and response.status: + if status_count is not None: + status_count -= 1 + cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) + status = response.status + + history = self.history + ( + RequestHistory(method, url, error, status, redirect_location), + ) + + new_retry = self.new( + total=total, + connect=connect, + read=read, + redirect=redirect, + status=status_count, + other=other, + history=history, + ) + + if new_retry.is_exhausted(): + reason = error or ResponseError(cause) + > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError + + During handling of the above exception, another exception occurred: + + self = , msg_id = None + timeout = None + + def _check_if_message_on_server(self, msg_id, timeout=None): + """Try to load page for specific message. If there is a html tag like then there is no + such message. + + :param msg_id: ID of message to be checked + :params timeout: The value of timeout to be passed to the get request + :return: + """ + + request_headers = dict() + if self._user or self._password: + request_headers['Cookie'] = self._make_user_and_pswd_cookie() + try: + > response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False, + verify=False, timeout=timeout) + + slic/utils/logbook.py:570: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:73: in get + return request("get", url, params=params, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request + return session.request(method=method, url=url, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request + resp = self.send(prep, **send_kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send + r = adapter.send(request, **kwargs) + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + except (ProtocolError, OSError) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + + > raise ConnectionError(e, request=request) + E requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError + + During handling of the above exception, another exception occurred: + + def test_get_default_elog_instance_with_direct_password_and_real_check(): url = "http://localhost:8080/demo" user = "robot" password = "testpassword" @@ -85,56 +1551,51 @@ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ slic/utils/elog.py:17: in post return self._log.post(*args, **kwargs) - slic/utils/logbook.py:273: in post - resp_message, resp_headers, resp_msg_id = _validate_response(response) + slic/utils/logbook.py:289: in post + self._check_if_message_on_server(msg_id) # Raises exceptions if no message or no response from server _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - response = + self = , msg_id = None + timeout = None - def _validate_response(response): - """ Validate response of the request.""" - print("[DEBUG] _validate_response called") - print(f" HTTP status code : {response.status_code}") - print(f" Response headers : {response.headers}") - print(f" First 300 bytes of content:\n{response.content[:300]!r}") + def _check_if_message_on_server(self, msg_id, timeout=None): + """Try to load page for specific message. If there is a html tag like then there is no + such message. - msg_id = None + :param msg_id: ID of message to be checked + :params timeout: The value of timeout to be passed to the get request + :return: + """ - if response.status_code not in [200, 302]: - print("[DEBUG] Status not 200/302 → searching for ") - err = re.findall('.*?', - response.content.decode('utf-8', 'ignore'), - flags=re.DOTALL) - if len(err) > 0: - err = re.sub('(?:<.*?>)', '', err[0]) - print(f"[DEBUG] Found error message: {err}") - if err: - raise LogbookMessageRejected('Rejected because of: ' + err) - else: - raise LogbookMessageRejected('Rejected because of unknown error.') - raise LogbookMessageRejected('Rejected because of unknown error.') - else: - location = response.headers.get('Location') - if location is not None: - print(f"[DEBUG] Location header: {location}") - if 'has moved' in location: - raise LogbookServerProblem('Logbook server has moved to another location.') - elif 'fail' in location: - raise LogbookAuthenticationError('Invalid username or password.') - else: - try: - msg_id = int(urllib.parse.urlsplit(location).path.split('/')[-1]) - print(f"[DEBUG] Parsed msg_id from Location: {msg_id}") - except ValueError: - print("[DEBUG] Could not parse msg_id from Location") - msg_id = None + request_headers = dict() + if self._user or self._password: + request_headers['Cookie'] = self._make_user_and_pswd_cookie() + try: + response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False, + verify=False, timeout=timeout) - if b'type=password' in response.content or b'type="password"' in response.content: - print("[DEBUG] Password input detected in response HTML — assuming login page → AUTH ERROR") - > raise LogbookAuthenticationError('Invalid username or password.') - E slic.utils.logbook_exceptions.LogbookAuthenticationError: Invalid username or password. + # If there is no message code 200 will be returned (OK) and _validate_response will not recognise it + # but there will be some error in the html code. + resp_message, resp_headers, resp_msg_id = _validate_response(response) + # If there is no message, code 200 will be returned (OK) but there will be some error indication in + # the html code. + if re.findall('.*?', + resp_message.decode('utf-8', 'ignore'), + flags=re.DOTALL): + raise LogbookInvalidMessageID('Message with ID: ' + str(msg_id) + ' does not exist on logbook.') + + except requests.Timeout as e: + # Catch here a timeout o the post request. + # Raise the logbook exception and let the user handle it + raise LogbookServerTimeout('{0} method cannot be completed because of a network timeout:\n' + + '{1}'.format(sys._getframe().f_code.co_name, e)) + + except requests.RequestException as e: + > raise LogbookServerProblem('No response from the logbook server.\nDetails: ' + '{0}'.format(e)) + E slic.utils.logbook_exceptions.LogbookServerProblem: No response from the logbook server. + E Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) - slic/utils/logbook.py:833: LogbookAuthenticationError + slic/utils/logbook.py:590: LogbookServerProblem During handling of the above exception, another exception occurred: @@ -150,7 +1611,8 @@ elog.post(text) except Exception as e: > pytest.fail(f"elog.open() raised an unexpected exception: {e}") - E Failed: elog.open() raised an unexpected exception: Invalid username or password. + E Failed: elog.open() raised an unexpected exception: No response from the logbook server. + E Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) tests/test_utils_elog.py:24: Failed ``` @@ -160,7 +1622,7 @@ **duration:** ```python - 0.0003204937092959881 + 0.00584752019494772 ``` **outcome:** @@ -178,7 +1640,7 @@ **duration:** ```python - 0.00013607600703835487 + 0.00016171811148524284 ``` **outcome:** @@ -192,7 +1654,7 @@ **duration:** ```python - 0.00034243473783135414 + 0.0003640740178525448 ``` **outcome:** @@ -237,7 +1699,7 @@ **duration:** ```python - 0.00016059307381510735 + 0.0002082372084259987 ``` **outcome:** @@ -255,7 +1717,7 @@ **duration:** ```python - 0.00012293364852666855 + 0.00017797807231545448 ``` **outcome:** @@ -269,7 +1731,7 @@ **duration:** ```python - 0.004283982794731855 + 0.005512538366019726 ``` **outcome:** @@ -283,7 +1745,8 @@ ```python path: /workspace/tligui_y/slic/tests/test_utils_elog.py lineno: 61 - message: Failed: elog.open() raised an unexpected exception: Invalid username or password. + message: Failed: elog.open() raised an unexpected exception: No response from the logbook server. + Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) ``` **traceback:** @@ -297,8 +1760,1473 @@ **longrepr:** ```python - mock_home = - mock_getpass = + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + > sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection + raise err + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + address = ('localhost', 8080), timeout = None, source_address = None + socket_options = [(6, 1, 1)] + + def create_connection( + address: tuple[str, int], + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + socket_options: _TYPE_SOCKET_OPTIONS | None = None, + ) -> socket.socket: + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`socket.getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith("["): + host = host.strip("[]") + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + try: + host.encode("idna") + except UnicodeError: + raise LocationParseError(f"'{host}', label empty or too long") from None + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not _DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + > sock.connect(sa) + E ConnectionRefusedError: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError + + The above exception was the direct cause of the following exception: + + self = + method = 'POST', url = '/demo/' + body = b'--f5f039deda62b157d5b094f465ce4716\r\nContent-Disposition: form-data; name="Author"\r\n\r\nrobot\r\n--f5f039deda62b1...Disposition: form-data; name="Text"; filename=""\r\n\r\nThis is a message2\r\n--f5f039deda62b157d5b094f465ce4716--\r\n' + headers = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '705', 'Content-Type': 'multipart/form-data; boundary=f5f039deda62b157d5b094f465ce4716'} + retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) + redirect = False, assert_same_host = False + timeout = Timeout(connect=None, read=None, total=None), pool_timeout = None + release_conn = False, chunked = False, body_pos = None, preload_content = False + decode_content = False, response_kw = {} + parsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/', query=None, fragment=None) + destination_scheme = None, conn = None, release_this_conn = True + http_tunnel_required = False, err = None, clean_exit = False + + def urlopen( # type: ignore[override] + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + redirect: bool = True, + assert_same_host: bool = True, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + pool_timeout: int | None = None, + release_conn: bool | None = None, + chunked: bool = False, + body_pos: _TYPE_BODY_POSITION | None = None, + preload_content: bool = True, + decode_content: bool = True, + **response_kw: typing.Any, + ) -> BaseHTTPResponse: + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method + such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When ``False``, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param bool preload_content: + If True, the response's body will be preloaded into memory. + + :param bool decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of ``preload_content`` + which defaults to ``True``. + + :param bool chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param int body_pos: + Position to seek to in file-like body in the event of a retry or + redirect. Typically this won't need to be set because urllib3 will + auto-populate the value when needed. + """ + parsed_url = parse_url(url) + destination_scheme = parsed_url.scheme + + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = preload_content + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + # Ensure that the URL we're connecting to is properly encoded + if url.startswith("/"): + url = to_str(_encode_target(url)) + else: + url = to_str(parsed_url.url) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] + release_this_conn = release_conn + + http_tunnel_required = connection_requires_http_tunnel( + self.proxy, self.proxy_config, destination_scheme + ) + + # Merge the proxy headers. Only done when not using HTTP CONNECT. We + # have to copy the headers dict so we can safely change it without those + # changes being reflected in anyone else's copy. + if not http_tunnel_required: + headers = headers.copy() # type: ignore[attr-defined] + headers.update(self.proxy_headers) # type: ignore[union-attr] + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + # Rewind body position, if needed. Record current position + # for future rewinds in the event of a redirect/retry. + body_pos = set_file_position(body, body_pos) + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] + + # Is this a closed/new connection that requires CONNECT tunnelling? + if self.proxy is not None and http_tunnel_required and conn.is_closed: + try: + self._prepare_proxy(conn) + except (BaseSSLError, OSError, SocketTimeout) as e: + self._raise_timeout( + err=e, url=self.proxy.url, timeout_value=conn.timeout + ) + raise + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Make the request on the HTTPConnection object + > response = self._make_request( + conn, + method, + url, + timeout=timeout_obj, + body=body, + headers=headers, + chunked=chunked, + retries=retries, + response_conn=response_conn, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request + conn.request( + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request + self.endheaders() + .pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + .pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output + self.send(msg) + .pixi/envs/default/lib/python3.8/http/client.py:951: in send + self.connect() + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect + self.sock = self._new_conn() + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + except socket.gaierror as e: + raise NameResolutionError(self.host, self, e) from e + except SocketTimeout as e: + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + + except OSError as e: + > raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + E urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError + + The above exception was the direct cause of the following exception: + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + > resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen + retries = retries.increment( + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = Retry(total=0, connect=None, read=False, redirect=None, status=None) + method = 'POST', url = '/demo/', response = None + error = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused') + _pool = + _stacktrace = + + def increment( + self, + method: str | None = None, + url: str | None = None, + response: BaseHTTPResponse | None = None, + error: Exception | None = None, + _pool: ConnectionPool | None = None, + _stacktrace: TracebackType | None = None, + ) -> Self: + """Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.BaseHTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + connect = self.connect + read = self.read + redirect = self.redirect + status_count = self.status + other = self.other + cause = "unknown" + status = None + redirect_location = None + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False or method is None or not self._is_method_retryable(method): + raise reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + + elif error: + # Other retry? + if other is not None: + other -= 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = "too many redirects" + response_redirect_location = response.get_redirect_location() + if response_redirect_location: + redirect_location = response_redirect_location + status = response.status + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and the given method is in the allowed_methods + cause = ResponseError.GENERIC_ERROR + if response and response.status: + if status_count is not None: + status_count -= 1 + cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) + status = response.status + + history = self.history + ( + RequestHistory(method, url, error, status, redirect_location), + ) + + new_retry = self.new( + total=total, + connect=connect, + read=read, + redirect=redirect, + status=status_count, + other=other, + history=history, + ) + + if new_retry.is_exhausted(): + reason = error or ResponseError(cause) + > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError + + During handling of the above exception, another exception occurred: + + self = + message = 'This is a message2', msg_id = None, reply = False + attributes = {'Author': 'robot', 'When': 1755182070, 'cmd': 'Submit', 'exp': 'demo', ...} + attachments = [], suppress_email_notification = False, encoding = None + timeout = None, kwargs = {'Author': 'robot'} + logbook_directory = 'elog_instance/logbooks/demo' + new_attachment_list = [('Text', ('', b'This is a message2'))] + objects_to_close = [] + attributes_to_edit = {'Author': b'robot', 'When': 1755182070, 'cmd': b'Submit', 'exp': b'demo', ...} + + def post(self, message, msg_id=None, reply=False, attributes=None, attachments=None, + suppress_email_notification=False, encoding=None, timeout=None, **kwargs): + """ + Posts message to the logbook. If msg_id is not specified new message will be created, otherwise existing + message will be edited, or a reply (if reply=True) to it will be created. This method returns the msg_id + of the newly created message. + """ + + logbook_directory = "elog_instance/logbooks/demo" + print(f"Checking the existence of the directory {logbook_directory}") + + # Check if the directory exists + if not os.path.exists(logbook_directory): + print(f"The directory {logbook_directory} does not exist.") + else: + print(f"The directory {logbook_directory} exists.") + + # Check write permissions + if os.access(logbook_directory, os.W_OK): + print(f"The directory {logbook_directory} has write permissions.") + else: + print(f"The directory {logbook_directory} does not have write permissions.") + + + print("STARTING POST") + # Ajout des impressions pour déboguer + print(f"Message to post: {message}") + print(f"msg_id: {msg_id}") + print(f"Attributes: {attributes}") + print(f"Attachments: {attachments}") + print(f"Encoding: {encoding}") + print(f"Timeout: {timeout}") + print(f"Additional kwargs: {kwargs}") + + attributes = attributes or {} + attributes = {**attributes, **kwargs} # kwargs as attributes with higher priority + print(f"Updated attributes: {attributes}") + + attachments = attachments or [] + print(f"Attachments list: {attachments}") + + if encoding is not None: + if encoding not in ['plain', 'HTML', 'ELCode']: + raise LogbookMessageRejected('Invalid message encoding. Valid options: plain, HTML, ELCode.') + attributes['Encoding'] = encoding + + if suppress_email_notification: + attributes["suppress"] = 1 + + # Prepare attachments + if attachments: + new_attachment_list, objects_to_close = self._prepare_attachments(attachments) + print(f"New attachments prepared: {new_attachment_list}") + else: + objects_to_close = [] + new_attachment_list = [] + + attributes_to_edit = dict() + + if msg_id: + print(f"Editing message with msg_id: {msg_id}") + if reply: + print(f"Replying to message with msg_id: {msg_id}") + self._check_if_message_on_server(msg_id) + attributes['reply_to'] = str(msg_id) + else: + print("Editing existing message.") + attributes['edit_id'] = str(msg_id) + attributes['skiplock'] = '1' + msg_to_edit, attributes_to_edit, existing_attachments_list = self.read(msg_id) + + # Merge new attributes + for attribute, data in attributes.items(): + if data is not None: + attributes_to_edit[attribute] = data + + print(f"Attributes after merging: {attributes_to_edit}") + + # Process existing attachments + i = 0 + existing_attachments_filename_list = [] + for attachment in existing_attachments_list: + attributes_to_edit[f'attachment{i}'] = os.path.basename(attachment) + existing_attachments_filename_list.append(os.path.basename(attachment)[14:]) + i += 1 + + print(f"Existing attachments: {existing_attachments_filename_list}") + + duplicate_attachment_list = [] + for new_attachment in new_attachment_list: + new_attachment_filename = new_attachment[1][0] + print(f"Checking new attachment: {new_attachment_filename}") + if new_attachment_filename in existing_attachments_filename_list: + # Same attachment exists on the server, compare content + new_attachment_content = new_attachment[1][1].read() + new_attachment[1][1].seek(0) + attachment_index = existing_attachments_filename_list.index(new_attachment_filename) + existing_attachment_content = self.download_attachment( + url=existing_attachments_list[attachment_index], + timeout=timeout + ) + if new_attachment_content == existing_attachment_content: + print(f"Duplicate attachment detected: {new_attachment_filename}") + duplicate_attachment_list.append(new_attachment) + else: + print(f"Attachment content has changed: {new_attachment_filename}") + self.delete_attachment(msg_id, attributes=attributes_to_edit, + attachment_id=attachment_index, + timeout=timeout, text=msg_to_edit) + existing_attachments_filename_list.pop(attachment_index) + existing_attachments_list.pop(attachment_index) + + print(f"Duplicate attachments to remove: {duplicate_attachment_list}") + + # Remove duplicates + for attach in duplicate_attachment_list: + new_attachment_list.remove(attach) + + print(f"Final new attachments list: {new_attachment_list}") + else: + # Creating a new message, add timestamp if not present + if 'When' not in attributes: + attributes['When'] = int(datetime.now().timestamp()) + + # Final check on attributes + if not attributes_to_edit: + attributes_to_edit = attributes + + print(f"Final attributes to send to the server: {attributes_to_edit}") + + # Remove reserved attributes + _remove_reserved_attributes(attributes_to_edit) + + new_attachment_list.append(('Text', ('', message.encode('iso-8859-1')))) + print(f"Final attachment list including message text: {new_attachment_list}") + + # Add base message attributes + self._add_base_msg_attributes(attributes_to_edit) + print(f"Attributes with base message added: {attributes_to_edit}") + + # Sanitize attribute keys + attributes_to_edit = _replace_special_characters_in_attribute_keys(attributes_to_edit) + print(f"Attributes after sanitizing keys: {attributes_to_edit}") + + # Encode all string values in latin1 + attributes_to_edit = _encode_values(attributes_to_edit) + print(f"Attributes after encoding: {attributes_to_edit}") + + try: + print("Sending POST request to the server...") + > response = requests.post(self._url, data=attributes_to_edit, files=new_attachment_list, + allow_redirects=False, verify=False, timeout=timeout) + + slic/utils/logbook.py:269: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:115: in post + return request("post", url, data=data, json=json, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request + return session.request(method=method, url=url, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request + resp = self.send(prep, **send_kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send + r = adapter.send(request, **kwargs) + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + except (ProtocolError, OSError) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + + > raise ConnectionError(e, request=request) + E requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError + + During handling of the above exception, another exception occurred: + + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + > sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection + raise err + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + address = ('localhost', 8080), timeout = None, source_address = None + socket_options = [(6, 1, 1)] + + def create_connection( + address: tuple[str, int], + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + socket_options: _TYPE_SOCKET_OPTIONS | None = None, + ) -> socket.socket: + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`socket.getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith("["): + host = host.strip("[]") + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + try: + host.encode("idna") + except UnicodeError: + raise LocationParseError(f"'{host}', label empty or too long") from None + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not _DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + > sock.connect(sa) + E ConnectionRefusedError: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError + + The above exception was the direct cause of the following exception: + + self = + method = 'GET', url = '/demo/None', body = None + headers = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'unm=robot;upwd=testpassword;'} + retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) + redirect = False, assert_same_host = False + timeout = Timeout(connect=None, read=None, total=None), pool_timeout = None + release_conn = False, chunked = False, body_pos = None, preload_content = False + decode_content = False, response_kw = {} + parsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/None', query=None, fragment=None) + destination_scheme = None, conn = None, release_this_conn = True + http_tunnel_required = False, err = None, clean_exit = False + + def urlopen( # type: ignore[override] + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + redirect: bool = True, + assert_same_host: bool = True, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + pool_timeout: int | None = None, + release_conn: bool | None = None, + chunked: bool = False, + body_pos: _TYPE_BODY_POSITION | None = None, + preload_content: bool = True, + decode_content: bool = True, + **response_kw: typing.Any, + ) -> BaseHTTPResponse: + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method + such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When ``False``, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param bool preload_content: + If True, the response's body will be preloaded into memory. + + :param bool decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of ``preload_content`` + which defaults to ``True``. + + :param bool chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param int body_pos: + Position to seek to in file-like body in the event of a retry or + redirect. Typically this won't need to be set because urllib3 will + auto-populate the value when needed. + """ + parsed_url = parse_url(url) + destination_scheme = parsed_url.scheme + + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = preload_content + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + # Ensure that the URL we're connecting to is properly encoded + if url.startswith("/"): + url = to_str(_encode_target(url)) + else: + url = to_str(parsed_url.url) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] + release_this_conn = release_conn + + http_tunnel_required = connection_requires_http_tunnel( + self.proxy, self.proxy_config, destination_scheme + ) + + # Merge the proxy headers. Only done when not using HTTP CONNECT. We + # have to copy the headers dict so we can safely change it without those + # changes being reflected in anyone else's copy. + if not http_tunnel_required: + headers = headers.copy() # type: ignore[attr-defined] + headers.update(self.proxy_headers) # type: ignore[union-attr] + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + # Rewind body position, if needed. Record current position + # for future rewinds in the event of a redirect/retry. + body_pos = set_file_position(body, body_pos) + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] + + # Is this a closed/new connection that requires CONNECT tunnelling? + if self.proxy is not None and http_tunnel_required and conn.is_closed: + try: + self._prepare_proxy(conn) + except (BaseSSLError, OSError, SocketTimeout) as e: + self._raise_timeout( + err=e, url=self.proxy.url, timeout_value=conn.timeout + ) + raise + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Make the request on the HTTPConnection object + > response = self._make_request( + conn, + method, + url, + timeout=timeout_obj, + body=body, + headers=headers, + chunked=chunked, + retries=retries, + response_conn=response_conn, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request + conn.request( + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request + self.endheaders() + .pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + .pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output + self.send(msg) + .pixi/envs/default/lib/python3.8/http/client.py:951: in send + self.connect() + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect + self.sock = self._new_conn() + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + except socket.gaierror as e: + raise NameResolutionError(self.host, self, e) from e + except SocketTimeout as e: + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + + except OSError as e: + > raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + E urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError + + The above exception was the direct cause of the following exception: + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + > resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen + retries = retries.increment( + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = Retry(total=0, connect=None, read=False, redirect=None, status=None) + method = 'GET', url = '/demo/None', response = None + error = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused') + _pool = + _stacktrace = + + def increment( + self, + method: str | None = None, + url: str | None = None, + response: BaseHTTPResponse | None = None, + error: Exception | None = None, + _pool: ConnectionPool | None = None, + _stacktrace: TracebackType | None = None, + ) -> Self: + """Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.BaseHTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + connect = self.connect + read = self.read + redirect = self.redirect + status_count = self.status + other = self.other + cause = "unknown" + status = None + redirect_location = None + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False or method is None or not self._is_method_retryable(method): + raise reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + + elif error: + # Other retry? + if other is not None: + other -= 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = "too many redirects" + response_redirect_location = response.get_redirect_location() + if response_redirect_location: + redirect_location = response_redirect_location + status = response.status + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and the given method is in the allowed_methods + cause = ResponseError.GENERIC_ERROR + if response and response.status: + if status_count is not None: + status_count -= 1 + cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) + status = response.status + + history = self.history + ( + RequestHistory(method, url, error, status, redirect_location), + ) + + new_retry = self.new( + total=total, + connect=connect, + read=read, + redirect=redirect, + status=status_count, + other=other, + history=history, + ) + + if new_retry.is_exhausted(): + reason = error or ResponseError(cause) + > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError + + During handling of the above exception, another exception occurred: + + self = , msg_id = None + timeout = None + + def _check_if_message_on_server(self, msg_id, timeout=None): + """Try to load page for specific message. If there is a html tag like then there is no + such message. + + :param msg_id: ID of message to be checked + :params timeout: The value of timeout to be passed to the get request + :return: + """ + + request_headers = dict() + if self._user or self._password: + request_headers['Cookie'] = self._make_user_and_pswd_cookie() + try: + > response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False, + verify=False, timeout=timeout) + + slic/utils/logbook.py:570: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:73: in get + return request("get", url, params=params, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request + return session.request(method=method, url=url, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request + resp = self.send(prep, **send_kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send + r = adapter.send(request, **kwargs) + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + except (ProtocolError, OSError) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + + > raise ConnectionError(e, request=request) + E requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError + + During handling of the above exception, another exception occurred: + + mock_home = + mock_getpass = @patch("slic.utils.elog.getpass") @patch("slic.utils.elog.Path.home") @@ -318,61 +3246,56 @@ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ slic/utils/elog.py:17: in post return self._log.post(*args, **kwargs) - slic/utils/logbook.py:273: in post - resp_message, resp_headers, resp_msg_id = _validate_response(response) + slic/utils/logbook.py:289: in post + self._check_if_message_on_server(msg_id) # Raises exceptions if no message or no response from server _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - response = + self = , msg_id = None + timeout = None - def _validate_response(response): - """ Validate response of the request.""" - print("[DEBUG] _validate_response called") - print(f" HTTP status code : {response.status_code}") - print(f" Response headers : {response.headers}") - print(f" First 300 bytes of content:\n{response.content[:300]!r}") + def _check_if_message_on_server(self, msg_id, timeout=None): + """Try to load page for specific message. If there is a html tag like then there is no + such message. - msg_id = None + :param msg_id: ID of message to be checked + :params timeout: The value of timeout to be passed to the get request + :return: + """ - if response.status_code not in [200, 302]: - print("[DEBUG] Status not 200/302 → searching for ") - err = re.findall('.*?', - response.content.decode('utf-8', 'ignore'), - flags=re.DOTALL) - if len(err) > 0: - err = re.sub('(?:<.*?>)', '', err[0]) - print(f"[DEBUG] Found error message: {err}") - if err: - raise LogbookMessageRejected('Rejected because of: ' + err) - else: - raise LogbookMessageRejected('Rejected because of unknown error.') - raise LogbookMessageRejected('Rejected because of unknown error.') - else: - location = response.headers.get('Location') - if location is not None: - print(f"[DEBUG] Location header: {location}") - if 'has moved' in location: - raise LogbookServerProblem('Logbook server has moved to another location.') - elif 'fail' in location: - raise LogbookAuthenticationError('Invalid username or password.') - else: - try: - msg_id = int(urllib.parse.urlsplit(location).path.split('/')[-1]) - print(f"[DEBUG] Parsed msg_id from Location: {msg_id}") - except ValueError: - print("[DEBUG] Could not parse msg_id from Location") - msg_id = None + request_headers = dict() + if self._user or self._password: + request_headers['Cookie'] = self._make_user_and_pswd_cookie() + try: + response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False, + verify=False, timeout=timeout) - if b'type=password' in response.content or b'type="password"' in response.content: - print("[DEBUG] Password input detected in response HTML — assuming login page → AUTH ERROR") - > raise LogbookAuthenticationError('Invalid username or password.') - E slic.utils.logbook_exceptions.LogbookAuthenticationError: Invalid username or password. + # If there is no message code 200 will be returned (OK) and _validate_response will not recognise it + # but there will be some error in the html code. + resp_message, resp_headers, resp_msg_id = _validate_response(response) + # If there is no message, code 200 will be returned (OK) but there will be some error indication in + # the html code. + if re.findall('.*?', + resp_message.decode('utf-8', 'ignore'), + flags=re.DOTALL): + raise LogbookInvalidMessageID('Message with ID: ' + str(msg_id) + ' does not exist on logbook.') + + except requests.Timeout as e: + # Catch here a timeout o the post request. + # Raise the logbook exception and let the user handle it + raise LogbookServerTimeout('{0} method cannot be completed because of a network timeout:\n' + + '{1}'.format(sys._getframe().f_code.co_name, e)) + + except requests.RequestException as e: + > raise LogbookServerProblem('No response from the logbook server.\nDetails: ' + '{0}'.format(e)) + E slic.utils.logbook_exceptions.LogbookServerProblem: No response from the logbook server. + E Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) - slic/utils/logbook.py:833: LogbookAuthenticationError + slic/utils/logbook.py:590: LogbookServerProblem During handling of the above exception, another exception occurred: - mock_home = - mock_getpass = + mock_home = + mock_getpass = @patch("slic.utils.elog.getpass") @patch("slic.utils.elog.Path.home") @@ -389,7 +3312,8 @@ elog.post(text) except Exception as e: > pytest.fail(f"elog.open() raised an unexpected exception: {e}") - E Failed: elog.open() raised an unexpected exception: Invalid username or password. + E Failed: elog.open() raised an unexpected exception: No response from the logbook server. + E Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) tests/test_utils_elog.py:61: Failed ``` @@ -399,7 +3323,7 @@ **duration:** ```python - 0.0002163117751479149 + 0.0002443208359181881 ``` **outcome:** @@ -417,7 +3341,7 @@ **duration:** ```python - 0.00013341102749109268 + 0.00014232704415917397 ``` **outcome:** @@ -431,7 +3355,7 @@ **duration:** ```python - 0.00466856500133872 + 0.006054379045963287 ``` **outcome:** @@ -445,7 +3369,8 @@ ```python path: /workspace/tligui_y/slic/tests/test_utils_elog.py lineno: 95 - message: Failed: elog.open() raised an unexpected exception: Invalid username or password. + message: Failed: elog.open() raised an unexpected exception: No response from the logbook server. + Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) ``` **traceback:** @@ -459,9 +3384,1474 @@ **longrepr:** ```python - mock_home = - mock_getuser = - mock_getpass = + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + > sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection + raise err + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + address = ('localhost', 8080), timeout = None, source_address = None + socket_options = [(6, 1, 1)] + + def create_connection( + address: tuple[str, int], + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + socket_options: _TYPE_SOCKET_OPTIONS | None = None, + ) -> socket.socket: + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`socket.getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith("["): + host = host.strip("[]") + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + try: + host.encode("idna") + except UnicodeError: + raise LocationParseError(f"'{host}', label empty or too long") from None + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not _DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + > sock.connect(sa) + E ConnectionRefusedError: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError + + The above exception was the direct cause of the following exception: + + self = + method = 'POST', url = '/demo/' + body = b'--9c8911db8eca06c93edc0908426a993c\r\nContent-Disposition: form-data; name="Author"\r\n\r\nrobot\r\n--9c8911db8eca06...Disposition: form-data; name="Text"; filename=""\r\n\r\nThis is a message3\r\n--9c8911db8eca06c93edc0908426a993c--\r\n' + headers = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '705', 'Content-Type': 'multipart/form-data; boundary=9c8911db8eca06c93edc0908426a993c'} + retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) + redirect = False, assert_same_host = False + timeout = Timeout(connect=None, read=None, total=None), pool_timeout = None + release_conn = False, chunked = False, body_pos = None, preload_content = False + decode_content = False, response_kw = {} + parsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/', query=None, fragment=None) + destination_scheme = None, conn = None, release_this_conn = True + http_tunnel_required = False, err = None, clean_exit = False + + def urlopen( # type: ignore[override] + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + redirect: bool = True, + assert_same_host: bool = True, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + pool_timeout: int | None = None, + release_conn: bool | None = None, + chunked: bool = False, + body_pos: _TYPE_BODY_POSITION | None = None, + preload_content: bool = True, + decode_content: bool = True, + **response_kw: typing.Any, + ) -> BaseHTTPResponse: + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method + such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When ``False``, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param bool preload_content: + If True, the response's body will be preloaded into memory. + + :param bool decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of ``preload_content`` + which defaults to ``True``. + + :param bool chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param int body_pos: + Position to seek to in file-like body in the event of a retry or + redirect. Typically this won't need to be set because urllib3 will + auto-populate the value when needed. + """ + parsed_url = parse_url(url) + destination_scheme = parsed_url.scheme + + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = preload_content + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + # Ensure that the URL we're connecting to is properly encoded + if url.startswith("/"): + url = to_str(_encode_target(url)) + else: + url = to_str(parsed_url.url) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] + release_this_conn = release_conn + + http_tunnel_required = connection_requires_http_tunnel( + self.proxy, self.proxy_config, destination_scheme + ) + + # Merge the proxy headers. Only done when not using HTTP CONNECT. We + # have to copy the headers dict so we can safely change it without those + # changes being reflected in anyone else's copy. + if not http_tunnel_required: + headers = headers.copy() # type: ignore[attr-defined] + headers.update(self.proxy_headers) # type: ignore[union-attr] + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + # Rewind body position, if needed. Record current position + # for future rewinds in the event of a redirect/retry. + body_pos = set_file_position(body, body_pos) + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] + + # Is this a closed/new connection that requires CONNECT tunnelling? + if self.proxy is not None and http_tunnel_required and conn.is_closed: + try: + self._prepare_proxy(conn) + except (BaseSSLError, OSError, SocketTimeout) as e: + self._raise_timeout( + err=e, url=self.proxy.url, timeout_value=conn.timeout + ) + raise + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Make the request on the HTTPConnection object + > response = self._make_request( + conn, + method, + url, + timeout=timeout_obj, + body=body, + headers=headers, + chunked=chunked, + retries=retries, + response_conn=response_conn, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request + conn.request( + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request + self.endheaders() + .pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + .pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output + self.send(msg) + .pixi/envs/default/lib/python3.8/http/client.py:951: in send + self.connect() + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect + self.sock = self._new_conn() + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + except socket.gaierror as e: + raise NameResolutionError(self.host, self, e) from e + except SocketTimeout as e: + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + + except OSError as e: + > raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + E urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError + + The above exception was the direct cause of the following exception: + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + > resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen + retries = retries.increment( + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = Retry(total=0, connect=None, read=False, redirect=None, status=None) + method = 'POST', url = '/demo/', response = None + error = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused') + _pool = + _stacktrace = + + def increment( + self, + method: str | None = None, + url: str | None = None, + response: BaseHTTPResponse | None = None, + error: Exception | None = None, + _pool: ConnectionPool | None = None, + _stacktrace: TracebackType | None = None, + ) -> Self: + """Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.BaseHTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + connect = self.connect + read = self.read + redirect = self.redirect + status_count = self.status + other = self.other + cause = "unknown" + status = None + redirect_location = None + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False or method is None or not self._is_method_retryable(method): + raise reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + + elif error: + # Other retry? + if other is not None: + other -= 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = "too many redirects" + response_redirect_location = response.get_redirect_location() + if response_redirect_location: + redirect_location = response_redirect_location + status = response.status + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and the given method is in the allowed_methods + cause = ResponseError.GENERIC_ERROR + if response and response.status: + if status_count is not None: + status_count -= 1 + cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) + status = response.status + + history = self.history + ( + RequestHistory(method, url, error, status, redirect_location), + ) + + new_retry = self.new( + total=total, + connect=connect, + read=read, + redirect=redirect, + status=status_count, + other=other, + history=history, + ) + + if new_retry.is_exhausted(): + reason = error or ResponseError(cause) + > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError + + During handling of the above exception, another exception occurred: + + self = + message = 'This is a message3', msg_id = None, reply = False + attributes = {'Author': 'robot', 'When': 1755182071, 'cmd': 'Submit', 'exp': 'demo', ...} + attachments = [], suppress_email_notification = False, encoding = None + timeout = None, kwargs = {'Author': 'robot'} + logbook_directory = 'elog_instance/logbooks/demo' + new_attachment_list = [('Text', ('', b'This is a message3'))] + objects_to_close = [] + attributes_to_edit = {'Author': b'robot', 'When': 1755182071, 'cmd': b'Submit', 'exp': b'demo', ...} + + def post(self, message, msg_id=None, reply=False, attributes=None, attachments=None, + suppress_email_notification=False, encoding=None, timeout=None, **kwargs): + """ + Posts message to the logbook. If msg_id is not specified new message will be created, otherwise existing + message will be edited, or a reply (if reply=True) to it will be created. This method returns the msg_id + of the newly created message. + """ + + logbook_directory = "elog_instance/logbooks/demo" + print(f"Checking the existence of the directory {logbook_directory}") + + # Check if the directory exists + if not os.path.exists(logbook_directory): + print(f"The directory {logbook_directory} does not exist.") + else: + print(f"The directory {logbook_directory} exists.") + + # Check write permissions + if os.access(logbook_directory, os.W_OK): + print(f"The directory {logbook_directory} has write permissions.") + else: + print(f"The directory {logbook_directory} does not have write permissions.") + + + print("STARTING POST") + # Ajout des impressions pour déboguer + print(f"Message to post: {message}") + print(f"msg_id: {msg_id}") + print(f"Attributes: {attributes}") + print(f"Attachments: {attachments}") + print(f"Encoding: {encoding}") + print(f"Timeout: {timeout}") + print(f"Additional kwargs: {kwargs}") + + attributes = attributes or {} + attributes = {**attributes, **kwargs} # kwargs as attributes with higher priority + print(f"Updated attributes: {attributes}") + + attachments = attachments or [] + print(f"Attachments list: {attachments}") + + if encoding is not None: + if encoding not in ['plain', 'HTML', 'ELCode']: + raise LogbookMessageRejected('Invalid message encoding. Valid options: plain, HTML, ELCode.') + attributes['Encoding'] = encoding + + if suppress_email_notification: + attributes["suppress"] = 1 + + # Prepare attachments + if attachments: + new_attachment_list, objects_to_close = self._prepare_attachments(attachments) + print(f"New attachments prepared: {new_attachment_list}") + else: + objects_to_close = [] + new_attachment_list = [] + + attributes_to_edit = dict() + + if msg_id: + print(f"Editing message with msg_id: {msg_id}") + if reply: + print(f"Replying to message with msg_id: {msg_id}") + self._check_if_message_on_server(msg_id) + attributes['reply_to'] = str(msg_id) + else: + print("Editing existing message.") + attributes['edit_id'] = str(msg_id) + attributes['skiplock'] = '1' + msg_to_edit, attributes_to_edit, existing_attachments_list = self.read(msg_id) + + # Merge new attributes + for attribute, data in attributes.items(): + if data is not None: + attributes_to_edit[attribute] = data + + print(f"Attributes after merging: {attributes_to_edit}") + + # Process existing attachments + i = 0 + existing_attachments_filename_list = [] + for attachment in existing_attachments_list: + attributes_to_edit[f'attachment{i}'] = os.path.basename(attachment) + existing_attachments_filename_list.append(os.path.basename(attachment)[14:]) + i += 1 + + print(f"Existing attachments: {existing_attachments_filename_list}") + + duplicate_attachment_list = [] + for new_attachment in new_attachment_list: + new_attachment_filename = new_attachment[1][0] + print(f"Checking new attachment: {new_attachment_filename}") + if new_attachment_filename in existing_attachments_filename_list: + # Same attachment exists on the server, compare content + new_attachment_content = new_attachment[1][1].read() + new_attachment[1][1].seek(0) + attachment_index = existing_attachments_filename_list.index(new_attachment_filename) + existing_attachment_content = self.download_attachment( + url=existing_attachments_list[attachment_index], + timeout=timeout + ) + if new_attachment_content == existing_attachment_content: + print(f"Duplicate attachment detected: {new_attachment_filename}") + duplicate_attachment_list.append(new_attachment) + else: + print(f"Attachment content has changed: {new_attachment_filename}") + self.delete_attachment(msg_id, attributes=attributes_to_edit, + attachment_id=attachment_index, + timeout=timeout, text=msg_to_edit) + existing_attachments_filename_list.pop(attachment_index) + existing_attachments_list.pop(attachment_index) + + print(f"Duplicate attachments to remove: {duplicate_attachment_list}") + + # Remove duplicates + for attach in duplicate_attachment_list: + new_attachment_list.remove(attach) + + print(f"Final new attachments list: {new_attachment_list}") + else: + # Creating a new message, add timestamp if not present + if 'When' not in attributes: + attributes['When'] = int(datetime.now().timestamp()) + + # Final check on attributes + if not attributes_to_edit: + attributes_to_edit = attributes + + print(f"Final attributes to send to the server: {attributes_to_edit}") + + # Remove reserved attributes + _remove_reserved_attributes(attributes_to_edit) + + new_attachment_list.append(('Text', ('', message.encode('iso-8859-1')))) + print(f"Final attachment list including message text: {new_attachment_list}") + + # Add base message attributes + self._add_base_msg_attributes(attributes_to_edit) + print(f"Attributes with base message added: {attributes_to_edit}") + + # Sanitize attribute keys + attributes_to_edit = _replace_special_characters_in_attribute_keys(attributes_to_edit) + print(f"Attributes after sanitizing keys: {attributes_to_edit}") + + # Encode all string values in latin1 + attributes_to_edit = _encode_values(attributes_to_edit) + print(f"Attributes after encoding: {attributes_to_edit}") + + try: + print("Sending POST request to the server...") + > response = requests.post(self._url, data=attributes_to_edit, files=new_attachment_list, + allow_redirects=False, verify=False, timeout=timeout) + + slic/utils/logbook.py:269: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:115: in post + return request("post", url, data=data, json=json, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request + return session.request(method=method, url=url, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request + resp = self.send(prep, **send_kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send + r = adapter.send(request, **kwargs) + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + except (ProtocolError, OSError) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + + > raise ConnectionError(e, request=request) + E requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError + + During handling of the above exception, another exception occurred: + + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + > sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection + raise err + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + address = ('localhost', 8080), timeout = None, source_address = None + socket_options = [(6, 1, 1)] + + def create_connection( + address: tuple[str, int], + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + socket_options: _TYPE_SOCKET_OPTIONS | None = None, + ) -> socket.socket: + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`socket.getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith("["): + host = host.strip("[]") + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + try: + host.encode("idna") + except UnicodeError: + raise LocationParseError(f"'{host}', label empty or too long") from None + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not _DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + > sock.connect(sa) + E ConnectionRefusedError: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError + + The above exception was the direct cause of the following exception: + + self = + method = 'GET', url = '/demo/None', body = None + headers = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'unm=robot;upwd=testpassword;'} + retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) + redirect = False, assert_same_host = False + timeout = Timeout(connect=None, read=None, total=None), pool_timeout = None + release_conn = False, chunked = False, body_pos = None, preload_content = False + decode_content = False, response_kw = {} + parsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/None', query=None, fragment=None) + destination_scheme = None, conn = None, release_this_conn = True + http_tunnel_required = False, err = None, clean_exit = False + + def urlopen( # type: ignore[override] + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + redirect: bool = True, + assert_same_host: bool = True, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + pool_timeout: int | None = None, + release_conn: bool | None = None, + chunked: bool = False, + body_pos: _TYPE_BODY_POSITION | None = None, + preload_content: bool = True, + decode_content: bool = True, + **response_kw: typing.Any, + ) -> BaseHTTPResponse: + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method + such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When ``False``, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param bool preload_content: + If True, the response's body will be preloaded into memory. + + :param bool decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of ``preload_content`` + which defaults to ``True``. + + :param bool chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param int body_pos: + Position to seek to in file-like body in the event of a retry or + redirect. Typically this won't need to be set because urllib3 will + auto-populate the value when needed. + """ + parsed_url = parse_url(url) + destination_scheme = parsed_url.scheme + + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = preload_content + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + # Ensure that the URL we're connecting to is properly encoded + if url.startswith("/"): + url = to_str(_encode_target(url)) + else: + url = to_str(parsed_url.url) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] + release_this_conn = release_conn + + http_tunnel_required = connection_requires_http_tunnel( + self.proxy, self.proxy_config, destination_scheme + ) + + # Merge the proxy headers. Only done when not using HTTP CONNECT. We + # have to copy the headers dict so we can safely change it without those + # changes being reflected in anyone else's copy. + if not http_tunnel_required: + headers = headers.copy() # type: ignore[attr-defined] + headers.update(self.proxy_headers) # type: ignore[union-attr] + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + # Rewind body position, if needed. Record current position + # for future rewinds in the event of a redirect/retry. + body_pos = set_file_position(body, body_pos) + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] + + # Is this a closed/new connection that requires CONNECT tunnelling? + if self.proxy is not None and http_tunnel_required and conn.is_closed: + try: + self._prepare_proxy(conn) + except (BaseSSLError, OSError, SocketTimeout) as e: + self._raise_timeout( + err=e, url=self.proxy.url, timeout_value=conn.timeout + ) + raise + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Make the request on the HTTPConnection object + > response = self._make_request( + conn, + method, + url, + timeout=timeout_obj, + body=body, + headers=headers, + chunked=chunked, + retries=retries, + response_conn=response_conn, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request + conn.request( + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request + self.endheaders() + .pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + .pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output + self.send(msg) + .pixi/envs/default/lib/python3.8/http/client.py:951: in send + self.connect() + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect + self.sock = self._new_conn() + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + except socket.gaierror as e: + raise NameResolutionError(self.host, self, e) from e + except SocketTimeout as e: + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + + except OSError as e: + > raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + E urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError + + The above exception was the direct cause of the following exception: + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + > resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen + retries = retries.increment( + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = Retry(total=0, connect=None, read=False, redirect=None, status=None) + method = 'GET', url = '/demo/None', response = None + error = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused') + _pool = + _stacktrace = + + def increment( + self, + method: str | None = None, + url: str | None = None, + response: BaseHTTPResponse | None = None, + error: Exception | None = None, + _pool: ConnectionPool | None = None, + _stacktrace: TracebackType | None = None, + ) -> Self: + """Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.BaseHTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + connect = self.connect + read = self.read + redirect = self.redirect + status_count = self.status + other = self.other + cause = "unknown" + status = None + redirect_location = None + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False or method is None or not self._is_method_retryable(method): + raise reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + + elif error: + # Other retry? + if other is not None: + other -= 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = "too many redirects" + response_redirect_location = response.get_redirect_location() + if response_redirect_location: + redirect_location = response_redirect_location + status = response.status + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and the given method is in the allowed_methods + cause = ResponseError.GENERIC_ERROR + if response and response.status: + if status_count is not None: + status_count -= 1 + cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) + status = response.status + + history = self.history + ( + RequestHistory(method, url, error, status, redirect_location), + ) + + new_retry = self.new( + total=total, + connect=connect, + read=read, + redirect=redirect, + status=status_count, + other=other, + history=history, + ) + + if new_retry.is_exhausted(): + reason = error or ResponseError(cause) + > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError + + During handling of the above exception, another exception occurred: + + self = , msg_id = None + timeout = None + + def _check_if_message_on_server(self, msg_id, timeout=None): + """Try to load page for specific message. If there is a html tag like then there is no + such message. + + :param msg_id: ID of message to be checked + :params timeout: The value of timeout to be passed to the get request + :return: + """ + + request_headers = dict() + if self._user or self._password: + request_headers['Cookie'] = self._make_user_and_pswd_cookie() + try: + > response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False, + verify=False, timeout=timeout) + + slic/utils/logbook.py:570: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:73: in get + return request("get", url, params=params, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request + return session.request(method=method, url=url, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request + resp = self.send(prep, **send_kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send + r = adapter.send(request, **kwargs) + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + except (ProtocolError, OSError) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + + > raise ConnectionError(e, request=request) + E requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError + + During handling of the above exception, another exception occurred: + + mock_home = + mock_getuser = + mock_getpass = @patch("slic.utils.elog.getpass") @patch("slic.utils.elog.getuser") @@ -489,62 +4879,57 @@ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ slic/utils/elog.py:17: in post return self._log.post(*args, **kwargs) - slic/utils/logbook.py:273: in post - resp_message, resp_headers, resp_msg_id = _validate_response(response) + slic/utils/logbook.py:289: in post + self._check_if_message_on_server(msg_id) # Raises exceptions if no message or no response from server _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - response = + self = , msg_id = None + timeout = None - def _validate_response(response): - """ Validate response of the request.""" - print("[DEBUG] _validate_response called") - print(f" HTTP status code : {response.status_code}") - print(f" Response headers : {response.headers}") - print(f" First 300 bytes of content:\n{response.content[:300]!r}") + def _check_if_message_on_server(self, msg_id, timeout=None): + """Try to load page for specific message. If there is a html tag like then there is no + such message. - msg_id = None + :param msg_id: ID of message to be checked + :params timeout: The value of timeout to be passed to the get request + :return: + """ - if response.status_code not in [200, 302]: - print("[DEBUG] Status not 200/302 → searching for ") - err = re.findall('.*?', - response.content.decode('utf-8', 'ignore'), - flags=re.DOTALL) - if len(err) > 0: - err = re.sub('(?:<.*?>)', '', err[0]) - print(f"[DEBUG] Found error message: {err}") - if err: - raise LogbookMessageRejected('Rejected because of: ' + err) - else: - raise LogbookMessageRejected('Rejected because of unknown error.') - raise LogbookMessageRejected('Rejected because of unknown error.') - else: - location = response.headers.get('Location') - if location is not None: - print(f"[DEBUG] Location header: {location}") - if 'has moved' in location: - raise LogbookServerProblem('Logbook server has moved to another location.') - elif 'fail' in location: - raise LogbookAuthenticationError('Invalid username or password.') - else: - try: - msg_id = int(urllib.parse.urlsplit(location).path.split('/')[-1]) - print(f"[DEBUG] Parsed msg_id from Location: {msg_id}") - except ValueError: - print("[DEBUG] Could not parse msg_id from Location") - msg_id = None + request_headers = dict() + if self._user or self._password: + request_headers['Cookie'] = self._make_user_and_pswd_cookie() + try: + response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False, + verify=False, timeout=timeout) - if b'type=password' in response.content or b'type="password"' in response.content: - print("[DEBUG] Password input detected in response HTML — assuming login page → AUTH ERROR") - > raise LogbookAuthenticationError('Invalid username or password.') - E slic.utils.logbook_exceptions.LogbookAuthenticationError: Invalid username or password. + # If there is no message code 200 will be returned (OK) and _validate_response will not recognise it + # but there will be some error in the html code. + resp_message, resp_headers, resp_msg_id = _validate_response(response) + # If there is no message, code 200 will be returned (OK) but there will be some error indication in + # the html code. + if re.findall('.*?', + resp_message.decode('utf-8', 'ignore'), + flags=re.DOTALL): + raise LogbookInvalidMessageID('Message with ID: ' + str(msg_id) + ' does not exist on logbook.') + + except requests.Timeout as e: + # Catch here a timeout o the post request. + # Raise the logbook exception and let the user handle it + raise LogbookServerTimeout('{0} method cannot be completed because of a network timeout:\n' + + '{1}'.format(sys._getframe().f_code.co_name, e)) + + except requests.RequestException as e: + > raise LogbookServerProblem('No response from the logbook server.\nDetails: ' + '{0}'.format(e)) + E slic.utils.logbook_exceptions.LogbookServerProblem: No response from the logbook server. + E Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) - slic/utils/logbook.py:833: LogbookAuthenticationError + slic/utils/logbook.py:590: LogbookServerProblem During handling of the above exception, another exception occurred: - mock_home = - mock_getuser = - mock_getpass = + mock_home = + mock_getuser = + mock_getpass = @patch("slic.utils.elog.getpass") @patch("slic.utils.elog.getuser") @@ -569,7 +4954,8 @@ elog.post(text) except Exception as e: > pytest.fail(f"elog.open() raised an unexpected exception: {e}") - E Failed: elog.open() raised an unexpected exception: Invalid username or password. + E Failed: elog.open() raised an unexpected exception: No response from the logbook server. + E Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) tests/test_utils_elog.py:95: Failed ``` @@ -579,7 +4965,7 @@ **duration:** ```python - 0.0002215951681137085 + 0.00028157979249954224 ``` **outcome:** @@ -597,7 +4983,7 @@ **duration:** ```python - 0.00013715913519263268 + 0.00014507630839943886 ``` **outcome:** @@ -611,7 +4997,7 @@ **duration:** ```python - 0.004666307009756565 + 0.007574480026960373 ``` **outcome:** @@ -624,8 +5010,9 @@ ```python path: /workspace/tligui_y/slic/slic/utils/logbook.py - lineno: 833 - message: slic.utils.logbook_exceptions.LogbookAuthenticationError: Invalid username or password. + lineno: 590 + message: slic.utils.logbook_exceptions.LogbookServerProblem: No response from the logbook server. + Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) ``` **traceback:** @@ -641,17 +5028,1482 @@ lineno: 17 message: in post - path: slic/utils/logbook.py - lineno: 273 + lineno: 289 message: in post - path: slic/utils/logbook.py - lineno: 833 - message: LogbookAuthenticationError + lineno: 590 + message: LogbookServerProblem ``` **longrepr:** ```python - mock_screenshot_class = + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + > sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection + raise err + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + address = ('localhost', 8080), timeout = None, source_address = None + socket_options = [(6, 1, 1)] + + def create_connection( + address: tuple[str, int], + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + socket_options: _TYPE_SOCKET_OPTIONS | None = None, + ) -> socket.socket: + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`socket.getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith("["): + host = host.strip("[]") + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + try: + host.encode("idna") + except UnicodeError: + raise LocationParseError(f"'{host}', label empty or too long") from None + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not _DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + > sock.connect(sa) + E ConnectionRefusedError: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError + + The above exception was the direct cause of the following exception: + + self = + method = 'POST', url = '/demo/' + body = b'--5cc3f9217327db39fecbda82305dbfff\r\nContent-Disposition: form-data; name="Author"\r\n\r\nrobot\r\n--5cc3f9217327db...-data; name="Text"; filename=""\r\n\r\nSCREENSHOT_INTEGRATION_TEST_MSG_456\r\n--5cc3f9217327db39fecbda82305dbfff--\r\n' + headers = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '858', 'Content-Type': 'multipart/form-data; boundary=5cc3f9217327db39fecbda82305dbfff'} + retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) + redirect = False, assert_same_host = False + timeout = Timeout(connect=None, read=None, total=None), pool_timeout = None + release_conn = False, chunked = False, body_pos = None, preload_content = False + decode_content = False, response_kw = {} + parsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/', query=None, fragment=None) + destination_scheme = None, conn = None, release_this_conn = True + http_tunnel_required = False, err = None, clean_exit = False + + def urlopen( # type: ignore[override] + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + redirect: bool = True, + assert_same_host: bool = True, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + pool_timeout: int | None = None, + release_conn: bool | None = None, + chunked: bool = False, + body_pos: _TYPE_BODY_POSITION | None = None, + preload_content: bool = True, + decode_content: bool = True, + **response_kw: typing.Any, + ) -> BaseHTTPResponse: + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method + such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When ``False``, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param bool preload_content: + If True, the response's body will be preloaded into memory. + + :param bool decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of ``preload_content`` + which defaults to ``True``. + + :param bool chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param int body_pos: + Position to seek to in file-like body in the event of a retry or + redirect. Typically this won't need to be set because urllib3 will + auto-populate the value when needed. + """ + parsed_url = parse_url(url) + destination_scheme = parsed_url.scheme + + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = preload_content + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + # Ensure that the URL we're connecting to is properly encoded + if url.startswith("/"): + url = to_str(_encode_target(url)) + else: + url = to_str(parsed_url.url) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] + release_this_conn = release_conn + + http_tunnel_required = connection_requires_http_tunnel( + self.proxy, self.proxy_config, destination_scheme + ) + + # Merge the proxy headers. Only done when not using HTTP CONNECT. We + # have to copy the headers dict so we can safely change it without those + # changes being reflected in anyone else's copy. + if not http_tunnel_required: + headers = headers.copy() # type: ignore[attr-defined] + headers.update(self.proxy_headers) # type: ignore[union-attr] + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + # Rewind body position, if needed. Record current position + # for future rewinds in the event of a redirect/retry. + body_pos = set_file_position(body, body_pos) + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] + + # Is this a closed/new connection that requires CONNECT tunnelling? + if self.proxy is not None and http_tunnel_required and conn.is_closed: + try: + self._prepare_proxy(conn) + except (BaseSSLError, OSError, SocketTimeout) as e: + self._raise_timeout( + err=e, url=self.proxy.url, timeout_value=conn.timeout + ) + raise + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Make the request on the HTTPConnection object + > response = self._make_request( + conn, + method, + url, + timeout=timeout_obj, + body=body, + headers=headers, + chunked=chunked, + retries=retries, + response_conn=response_conn, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request + conn.request( + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request + self.endheaders() + .pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + .pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output + self.send(msg) + .pixi/envs/default/lib/python3.8/http/client.py:951: in send + self.connect() + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect + self.sock = self._new_conn() + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + except socket.gaierror as e: + raise NameResolutionError(self.host, self, e) from e + except SocketTimeout as e: + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + + except OSError as e: + > raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + E urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError + + The above exception was the direct cause of the following exception: + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + > resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen + retries = retries.increment( + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = Retry(total=0, connect=None, read=False, redirect=None, status=None) + method = 'POST', url = '/demo/', response = None + error = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused') + _pool = + _stacktrace = + + def increment( + self, + method: str | None = None, + url: str | None = None, + response: BaseHTTPResponse | None = None, + error: Exception | None = None, + _pool: ConnectionPool | None = None, + _stacktrace: TracebackType | None = None, + ) -> Self: + """Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.BaseHTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + connect = self.connect + read = self.read + redirect = self.redirect + status_count = self.status + other = self.other + cause = "unknown" + status = None + redirect_location = None + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False or method is None or not self._is_method_retryable(method): + raise reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + + elif error: + # Other retry? + if other is not None: + other -= 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = "too many redirects" + response_redirect_location = response.get_redirect_location() + if response_redirect_location: + redirect_location = response_redirect_location + status = response.status + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and the given method is in the allowed_methods + cause = ResponseError.GENERIC_ERROR + if response and response.status: + if status_count is not None: + status_count -= 1 + cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) + status = response.status + + history = self.history + ( + RequestHistory(method, url, error, status, redirect_location), + ) + + new_retry = self.new( + total=total, + connect=connect, + read=read, + redirect=redirect, + status=status_count, + other=other, + history=history, + ) + + if new_retry.is_exhausted(): + reason = error or ResponseError(cause) + > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError + + During handling of the above exception, another exception occurred: + + self = + message = 'SCREENSHOT_INTEGRATION_TEST_MSG_456', msg_id = None, reply = False + attributes = {'Author': 'robot', 'When': 1755182071, 'cmd': 'Submit', 'exp': 'demo', ...} + attachments = ['/tmp/fake_screenshot.png'], suppress_email_notification = False + encoding = None, timeout = None, kwargs = {'Author': 'robot'} + logbook_directory = 'elog_instance/logbooks/demo' + new_attachment_list = [('attfile0', ('fake_screenshot.png', <_io.BufferedReader name='/tmp/fake_screenshot.png'>)), ('Text', ('', b'SCREENSHOT_INTEGRATION_TEST_MSG_456'))] + objects_to_close = [<_io.BufferedReader name='/tmp/fake_screenshot.png'>] + attributes_to_edit = {'Author': b'robot', 'When': 1755182071, 'cmd': b'Submit', 'exp': b'demo', ...} + + def post(self, message, msg_id=None, reply=False, attributes=None, attachments=None, + suppress_email_notification=False, encoding=None, timeout=None, **kwargs): + """ + Posts message to the logbook. If msg_id is not specified new message will be created, otherwise existing + message will be edited, or a reply (if reply=True) to it will be created. This method returns the msg_id + of the newly created message. + """ + + logbook_directory = "elog_instance/logbooks/demo" + print(f"Checking the existence of the directory {logbook_directory}") + + # Check if the directory exists + if not os.path.exists(logbook_directory): + print(f"The directory {logbook_directory} does not exist.") + else: + print(f"The directory {logbook_directory} exists.") + + # Check write permissions + if os.access(logbook_directory, os.W_OK): + print(f"The directory {logbook_directory} has write permissions.") + else: + print(f"The directory {logbook_directory} does not have write permissions.") + + + print("STARTING POST") + # Ajout des impressions pour déboguer + print(f"Message to post: {message}") + print(f"msg_id: {msg_id}") + print(f"Attributes: {attributes}") + print(f"Attachments: {attachments}") + print(f"Encoding: {encoding}") + print(f"Timeout: {timeout}") + print(f"Additional kwargs: {kwargs}") + + attributes = attributes or {} + attributes = {**attributes, **kwargs} # kwargs as attributes with higher priority + print(f"Updated attributes: {attributes}") + + attachments = attachments or [] + print(f"Attachments list: {attachments}") + + if encoding is not None: + if encoding not in ['plain', 'HTML', 'ELCode']: + raise LogbookMessageRejected('Invalid message encoding. Valid options: plain, HTML, ELCode.') + attributes['Encoding'] = encoding + + if suppress_email_notification: + attributes["suppress"] = 1 + + # Prepare attachments + if attachments: + new_attachment_list, objects_to_close = self._prepare_attachments(attachments) + print(f"New attachments prepared: {new_attachment_list}") + else: + objects_to_close = [] + new_attachment_list = [] + + attributes_to_edit = dict() + + if msg_id: + print(f"Editing message with msg_id: {msg_id}") + if reply: + print(f"Replying to message with msg_id: {msg_id}") + self._check_if_message_on_server(msg_id) + attributes['reply_to'] = str(msg_id) + else: + print("Editing existing message.") + attributes['edit_id'] = str(msg_id) + attributes['skiplock'] = '1' + msg_to_edit, attributes_to_edit, existing_attachments_list = self.read(msg_id) + + # Merge new attributes + for attribute, data in attributes.items(): + if data is not None: + attributes_to_edit[attribute] = data + + print(f"Attributes after merging: {attributes_to_edit}") + + # Process existing attachments + i = 0 + existing_attachments_filename_list = [] + for attachment in existing_attachments_list: + attributes_to_edit[f'attachment{i}'] = os.path.basename(attachment) + existing_attachments_filename_list.append(os.path.basename(attachment)[14:]) + i += 1 + + print(f"Existing attachments: {existing_attachments_filename_list}") + + duplicate_attachment_list = [] + for new_attachment in new_attachment_list: + new_attachment_filename = new_attachment[1][0] + print(f"Checking new attachment: {new_attachment_filename}") + if new_attachment_filename in existing_attachments_filename_list: + # Same attachment exists on the server, compare content + new_attachment_content = new_attachment[1][1].read() + new_attachment[1][1].seek(0) + attachment_index = existing_attachments_filename_list.index(new_attachment_filename) + existing_attachment_content = self.download_attachment( + url=existing_attachments_list[attachment_index], + timeout=timeout + ) + if new_attachment_content == existing_attachment_content: + print(f"Duplicate attachment detected: {new_attachment_filename}") + duplicate_attachment_list.append(new_attachment) + else: + print(f"Attachment content has changed: {new_attachment_filename}") + self.delete_attachment(msg_id, attributes=attributes_to_edit, + attachment_id=attachment_index, + timeout=timeout, text=msg_to_edit) + existing_attachments_filename_list.pop(attachment_index) + existing_attachments_list.pop(attachment_index) + + print(f"Duplicate attachments to remove: {duplicate_attachment_list}") + + # Remove duplicates + for attach in duplicate_attachment_list: + new_attachment_list.remove(attach) + + print(f"Final new attachments list: {new_attachment_list}") + else: + # Creating a new message, add timestamp if not present + if 'When' not in attributes: + attributes['When'] = int(datetime.now().timestamp()) + + # Final check on attributes + if not attributes_to_edit: + attributes_to_edit = attributes + + print(f"Final attributes to send to the server: {attributes_to_edit}") + + # Remove reserved attributes + _remove_reserved_attributes(attributes_to_edit) + + new_attachment_list.append(('Text', ('', message.encode('iso-8859-1')))) + print(f"Final attachment list including message text: {new_attachment_list}") + + # Add base message attributes + self._add_base_msg_attributes(attributes_to_edit) + print(f"Attributes with base message added: {attributes_to_edit}") + + # Sanitize attribute keys + attributes_to_edit = _replace_special_characters_in_attribute_keys(attributes_to_edit) + print(f"Attributes after sanitizing keys: {attributes_to_edit}") + + # Encode all string values in latin1 + attributes_to_edit = _encode_values(attributes_to_edit) + print(f"Attributes after encoding: {attributes_to_edit}") + + try: + print("Sending POST request to the server...") + > response = requests.post(self._url, data=attributes_to_edit, files=new_attachment_list, + allow_redirects=False, verify=False, timeout=timeout) + + slic/utils/logbook.py:269: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:115: in post + return request("post", url, data=data, json=json, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request + return session.request(method=method, url=url, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request + resp = self.send(prep, **send_kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send + r = adapter.send(request, **kwargs) + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + except (ProtocolError, OSError) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + + > raise ConnectionError(e, request=request) + E requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError + + During handling of the above exception, another exception occurred: + + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + > sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection + raise err + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + address = ('localhost', 8080), timeout = None, source_address = None + socket_options = [(6, 1, 1)] + + def create_connection( + address: tuple[str, int], + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + socket_options: _TYPE_SOCKET_OPTIONS | None = None, + ) -> socket.socket: + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`socket.getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith("["): + host = host.strip("[]") + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + try: + host.encode("idna") + except UnicodeError: + raise LocationParseError(f"'{host}', label empty or too long") from None + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not _DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + > sock.connect(sa) + E ConnectionRefusedError: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError + + The above exception was the direct cause of the following exception: + + self = + method = 'GET', url = '/demo/None', body = None + headers = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'unm=robot;upwd=testpassword;'} + retries = Retry(total=0, connect=None, read=False, redirect=None, status=None) + redirect = False, assert_same_host = False + timeout = Timeout(connect=None, read=None, total=None), pool_timeout = None + release_conn = False, chunked = False, body_pos = None, preload_content = False + decode_content = False, response_kw = {} + parsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/None', query=None, fragment=None) + destination_scheme = None, conn = None, release_this_conn = True + http_tunnel_required = False, err = None, clean_exit = False + + def urlopen( # type: ignore[override] + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + redirect: bool = True, + assert_same_host: bool = True, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + pool_timeout: int | None = None, + release_conn: bool | None = None, + chunked: bool = False, + body_pos: _TYPE_BODY_POSITION | None = None, + preload_content: bool = True, + decode_content: bool = True, + **response_kw: typing.Any, + ) -> BaseHTTPResponse: + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method + such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When ``False``, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param bool preload_content: + If True, the response's body will be preloaded into memory. + + :param bool decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of ``preload_content`` + which defaults to ``True``. + + :param bool chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param int body_pos: + Position to seek to in file-like body in the event of a retry or + redirect. Typically this won't need to be set because urllib3 will + auto-populate the value when needed. + """ + parsed_url = parse_url(url) + destination_scheme = parsed_url.scheme + + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = preload_content + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + # Ensure that the URL we're connecting to is properly encoded + if url.startswith("/"): + url = to_str(_encode_target(url)) + else: + url = to_str(parsed_url.url) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] + release_this_conn = release_conn + + http_tunnel_required = connection_requires_http_tunnel( + self.proxy, self.proxy_config, destination_scheme + ) + + # Merge the proxy headers. Only done when not using HTTP CONNECT. We + # have to copy the headers dict so we can safely change it without those + # changes being reflected in anyone else's copy. + if not http_tunnel_required: + headers = headers.copy() # type: ignore[attr-defined] + headers.update(self.proxy_headers) # type: ignore[union-attr] + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + # Rewind body position, if needed. Record current position + # for future rewinds in the event of a redirect/retry. + body_pos = set_file_position(body, body_pos) + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] + + # Is this a closed/new connection that requires CONNECT tunnelling? + if self.proxy is not None and http_tunnel_required and conn.is_closed: + try: + self._prepare_proxy(conn) + except (BaseSSLError, OSError, SocketTimeout) as e: + self._raise_timeout( + err=e, url=self.proxy.url, timeout_value=conn.timeout + ) + raise + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Make the request on the HTTPConnection object + > response = self._make_request( + conn, + method, + url, + timeout=timeout_obj, + body=body, + headers=headers, + chunked=chunked, + retries=retries, + response_conn=response_conn, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request + conn.request( + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request + self.endheaders() + .pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders + self._send_output(message_body, encode_chunked=encode_chunked) + .pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output + self.send(msg) + .pixi/envs/default/lib/python3.8/http/client.py:951: in send + self.connect() + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect + self.sock = self._new_conn() + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + except socket.gaierror as e: + raise NameResolutionError(self.host, self, e) from e + except SocketTimeout as e: + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + + except OSError as e: + > raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + E urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError + + The above exception was the direct cause of the following exception: + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + > resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen + retries = retries.increment( + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = Retry(total=0, connect=None, read=False, redirect=None, status=None) + method = 'GET', url = '/demo/None', response = None + error = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused') + _pool = + _stacktrace = + + def increment( + self, + method: str | None = None, + url: str | None = None, + response: BaseHTTPResponse | None = None, + error: Exception | None = None, + _pool: ConnectionPool | None = None, + _stacktrace: TracebackType | None = None, + ) -> Self: + """Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.BaseHTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + connect = self.connect + read = self.read + redirect = self.redirect + status_count = self.status + other = self.other + cause = "unknown" + status = None + redirect_location = None + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False or method is None or not self._is_method_retryable(method): + raise reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + + elif error: + # Other retry? + if other is not None: + other -= 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = "too many redirects" + response_redirect_location = response.get_redirect_location() + if response_redirect_location: + redirect_location = response_redirect_location + status = response.status + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and the given method is in the allowed_methods + cause = ResponseError.GENERIC_ERROR + if response and response.status: + if status_count is not None: + status_count -= 1 + cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) + status = response.status + + history = self.history + ( + RequestHistory(method, url, error, status, redirect_location), + ) + + new_retry = self.new( + total=total, + connect=connect, + read=read, + redirect=redirect, + status=status_count, + other=other, + history=history, + ) + + if new_retry.is_exhausted(): + reason = error or ResponseError(cause) + > raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + E urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError + + During handling of the above exception, another exception occurred: + + self = , msg_id = None + timeout = None + + def _check_if_message_on_server(self, msg_id, timeout=None): + """Try to load page for specific message. If there is a html tag like then there is no + such message. + + :param msg_id: ID of message to be checked + :params timeout: The value of timeout to be passed to the get request + :return: + """ + + request_headers = dict() + if self._user or self._password: + request_headers['Cookie'] = self._make_user_and_pswd_cookie() + try: + > response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False, + verify=False, timeout=timeout) + + slic/utils/logbook.py:570: + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:73: in get + return request("get", url, params=params, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request + return session.request(method=method, url=url, **kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request + resp = self.send(prep, **send_kwargs) + .pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send + r = adapter.send(request, **kwargs) + _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + self = + request = , stream = False + timeout = Timeout(connect=None, read=None, total=None), verify = False + cert = None, proxies = OrderedDict() + + def send( + self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None + ): + """Sends PreparedRequest object. Returns Response object. + + :param request: The :class:`PreparedRequest ` being sent. + :param stream: (optional) Whether to stream the request content. + :param timeout: (optional) How long to wait for the server to send + data before giving up, as a float, or a :ref:`(connect timeout, + read timeout) ` tuple. + :type timeout: float or tuple or urllib3 Timeout object + :param verify: (optional) Either a boolean, in which case it controls whether + we verify the server's TLS certificate, or a string, in which case it + must be a path to a CA bundle to use + :param cert: (optional) Any user-provided SSL certificate to be trusted. + :param proxies: (optional) The proxies dictionary to apply to the request. + :rtype: requests.Response + """ + + try: + conn = self.get_connection_with_tls_context( + request, verify, proxies=proxies, cert=cert + ) + except LocationValueError as e: + raise InvalidURL(e, request=request) + + self.cert_verify(conn, request.url, verify, cert) + url = self.request_url(request, proxies) + self.add_headers( + request, + stream=stream, + timeout=timeout, + verify=verify, + cert=cert, + proxies=proxies, + ) + + chunked = not (request.body is None or "Content-Length" in request.headers) + + if isinstance(timeout, tuple): + try: + connect, read = timeout + timeout = TimeoutSauce(connect=connect, read=read) + except ValueError: + raise ValueError( + f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " + f"or a single float to set both timeouts to the same value." + ) + elif isinstance(timeout, TimeoutSauce): + pass + else: + timeout = TimeoutSauce(connect=timeout, read=timeout) + + try: + resp = conn.urlopen( + method=request.method, + url=url, + body=request.body, + headers=request.headers, + redirect=False, + assert_same_host=False, + preload_content=False, + decode_content=False, + retries=self.max_retries, + timeout=timeout, + chunked=chunked, + ) + + except (ProtocolError, OSError) as err: + raise ConnectionError(err, request=request) + + except MaxRetryError as e: + if isinstance(e.reason, ConnectTimeoutError): + # TODO: Remove this in 3.0.0: see #2811 + if not isinstance(e.reason, NewConnectionError): + raise ConnectTimeout(e, request=request) + + if isinstance(e.reason, ResponseError): + raise RetryError(e, request=request) + + if isinstance(e.reason, _ProxyError): + raise ProxyError(e, request=request) + + if isinstance(e.reason, _SSLError): + # This branch is for urllib3 v1.22 and later. + raise SSLError(e, request=request) + + > raise ConnectionError(e, request=request) + E requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) + + .pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError + + During handling of the above exception, another exception occurred: + + mock_screenshot_class = @patch("slic.utils.elog.Screenshot") def test_screenshot(mock_screenshot_class): @@ -673,56 +6525,51 @@ self.post(message, **kwargs) slic/utils/elog.py:17: in post return self._log.post(*args, **kwargs) - slic/utils/logbook.py:273: in post - resp_message, resp_headers, resp_msg_id = _validate_response(response) + slic/utils/logbook.py:289: in post + self._check_if_message_on_server(msg_id) # Raises exceptions if no message or no response from server _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - response = + self = , msg_id = None + timeout = None - def _validate_response(response): - """ Validate response of the request.""" - print("[DEBUG] _validate_response called") - print(f" HTTP status code : {response.status_code}") - print(f" Response headers : {response.headers}") - print(f" First 300 bytes of content:\n{response.content[:300]!r}") + def _check_if_message_on_server(self, msg_id, timeout=None): + """Try to load page for specific message. If there is a html tag like then there is no + such message. - msg_id = None + :param msg_id: ID of message to be checked + :params timeout: The value of timeout to be passed to the get request + :return: + """ - if response.status_code not in [200, 302]: - print("[DEBUG] Status not 200/302 → searching for ") - err = re.findall('.*?', - response.content.decode('utf-8', 'ignore'), - flags=re.DOTALL) - if len(err) > 0: - err = re.sub('(?:<.*?>)', '', err[0]) - print(f"[DEBUG] Found error message: {err}") - if err: - raise LogbookMessageRejected('Rejected because of: ' + err) - else: - raise LogbookMessageRejected('Rejected because of unknown error.') - raise LogbookMessageRejected('Rejected because of unknown error.') - else: - location = response.headers.get('Location') - if location is not None: - print(f"[DEBUG] Location header: {location}") - if 'has moved' in location: - raise LogbookServerProblem('Logbook server has moved to another location.') - elif 'fail' in location: - raise LogbookAuthenticationError('Invalid username or password.') - else: - try: - msg_id = int(urllib.parse.urlsplit(location).path.split('/')[-1]) - print(f"[DEBUG] Parsed msg_id from Location: {msg_id}") - except ValueError: - print("[DEBUG] Could not parse msg_id from Location") - msg_id = None + request_headers = dict() + if self._user or self._password: + request_headers['Cookie'] = self._make_user_and_pswd_cookie() + try: + response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False, + verify=False, timeout=timeout) - if b'type=password' in response.content or b'type="password"' in response.content: - print("[DEBUG] Password input detected in response HTML — assuming login page → AUTH ERROR") - > raise LogbookAuthenticationError('Invalid username or password.') - E slic.utils.logbook_exceptions.LogbookAuthenticationError: Invalid username or password. + # If there is no message code 200 will be returned (OK) and _validate_response will not recognise it + # but there will be some error in the html code. + resp_message, resp_headers, resp_msg_id = _validate_response(response) + # If there is no message, code 200 will be returned (OK) but there will be some error indication in + # the html code. + if re.findall('.*?', + resp_message.decode('utf-8', 'ignore'), + flags=re.DOTALL): + raise LogbookInvalidMessageID('Message with ID: ' + str(msg_id) + ' does not exist on logbook.') + + except requests.Timeout as e: + # Catch here a timeout o the post request. + # Raise the logbook exception and let the user handle it + raise LogbookServerTimeout('{0} method cannot be completed because of a network timeout:\n' + + '{1}'.format(sys._getframe().f_code.co_name, e)) + + except requests.RequestException as e: + > raise LogbookServerProblem('No response from the logbook server.\nDetails: ' + '{0}'.format(e)) + E slic.utils.logbook_exceptions.LogbookServerProblem: No response from the logbook server. + E Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')) - slic/utils/logbook.py:833: LogbookAuthenticationError + slic/utils/logbook.py:590: LogbookServerProblem ``` **_*📌 Teardown phase*_** @@ -730,7 +6577,7 @@ **duration:** ```python - 0.0002205907367169857 + 0.00026907073333859444 ``` **outcome:** diff --git a/markdown/coverage-summary.md b/markdown/coverage-summary.md index a068c6935..0221d01cf 100644 --- a/markdown/coverage-summary.md +++ b/markdown/coverage-summary.md @@ -32741,3 +32741,230 @@ | slic/utils/utils.py | 17 | 8 | 53% | | slic/utils/xrange.py | 33 | 30 | 9% | | **TOTAL** | **9985** | **7253** | **27%** | +| Name | Stmts | Miss | Cover | +|----------------------------------------------- | -------: | -------: | ------: | +| slic/\_\_init\_\_.py | 20 | 2 | 90% | +| slic/core/\_\_init\_\_.py | 5 | 0 | 100% | +| slic/core/acquisition/\_\_init\_\_.py | 7 | 0 | 100% | +| slic/core/acquisition/acquisition.py | 56 | 42 | 25% | +| slic/core/acquisition/baseacquisition.py | 5 | 1 | 80% | +| slic/core/acquisition/broker/\_\_init\_\_.py | 2 | 0 | 100% | +| slic/core/acquisition/broker/brokerclient.py | 104 | 80 | 23% | +| slic/core/acquisition/broker/brokerconfig.py | 102 | 87 | 15% | +| slic/core/acquisition/broker/pedestal.py | 66 | 57 | 14% | +| slic/core/acquisition/broker/pids.py | 17 | 12 | 29% | +| slic/core/acquisition/broker/post\_retrieve.py | 120 | 120 | 0% | +| slic/core/acquisition/broker/requeststatus.py | 77 | 73 | 5% | +| slic/core/acquisition/broker/restapi.py | 147 | 107 | 27% | +| slic/core/acquisition/broker/tools.py | 52 | 18 | 65% | +| slic/core/acquisition/bsacquisition.py | 10 | 3 | 70% | +| slic/core/acquisition/bschannels.py | 30 | 19 | 37% | +| slic/core/acquisition/channels.py | 51 | 30 | 41% | +| slic/core/acquisition/dbacquisition.py | 19 | 12 | 37% | +| slic/core/acquisition/detcfg.py | 82 | 35 | 57% | +| slic/core/acquisition/diaacquisition.py | 111 | 111 | 0% | +| slic/core/acquisition/diaconfig.py | 36 | 36 | 0% | +| slic/core/acquisition/dummyacquisition.py | 12 | 12 | 0% | +| slic/core/acquisition/fakeacquisition.py | 76 | 53 | 30% | +| slic/core/acquisition/pedestals.py | 69 | 69 | 0% | +| slic/core/acquisition/pvacquisition.py | 60 | 47 | 22% | +| slic/core/acquisition/pvchannels.py | 13 | 7 | 46% | +| slic/core/acquisition/sfacquisition.py | 159 | 129 | 19% | +| slic/core/acquisition/sfpaths.py | 23 | 19 | 17% | +| slic/core/acquisition/spreadsheet.py | 45 | 45 | 0% | +| slic/core/adjustable/\_\_init\_\_.py | 11 | 0 | 100% | +| slic/core/adjustable/adjustable.py | 50 | 30 | 40% | +| slic/core/adjustable/baseadjustable.py | 28 | 18 | 36% | +| slic/core/adjustable/collection.py | 22 | 14 | 36% | +| slic/core/adjustable/combined.py | 15 | 8 | 47% | +| slic/core/adjustable/convenience.py | 35 | 20 | 43% | +| slic/core/adjustable/converted.py | 16 | 10 | 38% | +| slic/core/adjustable/dummyadjustable.py | 41 | 30 | 27% | +| slic/core/adjustable/error.py | 2 | 0 | 100% | +| slic/core/adjustable/genericadjustable.py | 32 | 24 | 25% | +| slic/core/adjustable/limited.py | 29 | 18 | 38% | +| slic/core/adjustable/linked.py | 22 | 15 | 32% | +| slic/core/adjustable/pvadjustable.py | 119 | 91 | 24% | +| slic/core/adjustable/pvchangemon.py | 77 | 56 | 27% | +| slic/core/adjustable/pvenumadjustable.py | 38 | 22 | 42% | +| slic/core/adjustable/scaler.py | 22 | 16 | 27% | +| slic/core/condition/\_\_init\_\_.py | 2 | 0 | 100% | +| slic/core/condition/basecondition.py | 8 | 2 | 75% | +| slic/core/condition/condition.py | 107 | 79 | 26% | +| slic/core/condition/pvcondition.py | 21 | 12 | 43% | +| slic/core/condition/valuecondition.py | 22 | 15 | 32% | +| slic/core/device/\_\_init\_\_.py | 2 | 0 | 100% | +| slic/core/device/auto.py | 12 | 12 | 0% | +| slic/core/device/basedevice.py | 2 | 0 | 100% | +| slic/core/device/device.py | 46 | 35 | 24% | +| slic/core/device/filtered.py | 23 | 23 | 0% | +| slic/core/device/simpledevice.py | 6 | 2 | 67% | +| slic/core/scanner/\_\_init\_\_.py | 1 | 0 | 100% | +| slic/core/scanner/runname.py | 36 | 23 | 36% | +| slic/core/scanner/scanbackend.py | 232 | 197 | 15% | +| slic/core/scanner/scaninfo.py | 45 | 35 | 22% | +| slic/core/scanner/scanner.py | 136 | 89 | 35% | +| slic/core/sensor/\_\_init\_\_.py | 8 | 0 | 100% | +| slic/core/sensor/basesensor.py | 12 | 3 | 75% | +| slic/core/sensor/bscombined.py | 9 | 5 | 44% | +| slic/core/sensor/bsmonitor.py | 102 | 73 | 28% | +| slic/core/sensor/bsnorm.py | 12 | 7 | 42% | +| slic/core/sensor/bssensor.py | 6 | 2 | 67% | +| slic/core/sensor/combined.py | 31 | 20 | 35% | +| slic/core/sensor/monitor.py | 62 | 51 | 18% | +| slic/core/sensor/norm.py | 9 | 5 | 44% | +| slic/core/sensor/pvsensor.py | 32 | 20 | 38% | +| slic/core/sensor/remoteplot.py | 15 | 10 | 33% | +| slic/core/sensor/sensor.py | 60 | 42 | 30% | +| slic/core/task/\_\_init\_\_.py | 4 | 0 | 100% | +| slic/core/task/basetask.py | 11 | 3 | 73% | +| slic/core/task/daqtask.py | 23 | 16 | 30% | +| slic/core/task/loop.py | 57 | 40 | 30% | +| slic/core/task/producer.py | 25 | 18 | 28% | +| slic/core/task/task.py | 62 | 46 | 26% | +| slic/devices/\_\_init\_\_.py | 7 | 0 | 100% | +| slic/devices/cameras/\_\_init\_\_.py | 4 | 0 | 100% | +| slic/devices/cameras/basler.py | 8 | 4 | 50% | +| slic/devices/cameras/camera\_bs.py | 13 | 8 | 38% | +| slic/devices/cameras/camera\_ca.py | 34 | 19 | 44% | +| slic/devices/cameras/camerabase.py | 17 | 12 | 29% | +| slic/devices/cameras/screenpanel.py | 31 | 21 | 32% | +| slic/devices/endstations/\_\_init\_\_.py | 3 | 0 | 100% | +| slic/devices/endstations/alvra\_flex.py | 10 | 5 | 50% | +| slic/devices/endstations/alvra\_huber.py | 8 | 4 | 50% | +| slic/devices/endstations/alvra\_prime.py | 48 | 34 | 29% | +| slic/devices/endstations/alvra\_xtg.py | 8 | 8 | 0% | +| slic/devices/endstations/bernina\_cameras.py | 33 | 33 | 0% | +| slic/devices/endstations/bernina\_platform.py | 46 | 46 | 0% | +| slic/devices/general/\_\_init\_\_.py | 4 | 0 | 100% | +| slic/devices/general/delay\_compensation.py | 13 | 13 | 0% | +| slic/devices/general/delay\_stage.py | 57 | 30 | 47% | +| slic/devices/general/detectors/\_\_init\_\_.py | 2 | 0 | 100% | +| slic/devices/general/detectors/buffer.py | 66 | 35 | 47% | +| slic/devices/general/detectors/digitizer.py | 13 | 7 | 46% | +| slic/devices/general/detectors/pvdatastream.py | 33 | 24 | 27% | +| slic/devices/general/detectors/timer.py | 15 | 9 | 40% | +| slic/devices/general/micosstage.py | 7 | 7 | 0% | +| slic/devices/general/motor.py | 128 | 88 | 31% | +| slic/devices/general/shutter.py | 22 | 12 | 45% | +| slic/devices/general/shutterctx.py | 18 | 7 | 61% | +| slic/devices/general/smaract.py | 169 | 125 | 26% | +| slic/devices/loptics/\_\_init\_\_.py | 2 | 0 | 100% | +| slic/devices/loptics/alvra\_explaser.py | 29 | 21 | 28% | +| slic/devices/loptics/bernina\_explaser.py | 28 | 28 | 0% | +| slic/devices/loptics/lasershutter.py | 22 | 14 | 36% | +| slic/devices/timing/\_\_init\_\_.py | 0 | 0 | 100% | +| slic/devices/timing/events/\_\_init\_\_.py | 3 | 3 | 0% | +| slic/devices/timing/events/codes.py | 5 | 5 | 0% | +| slic/devices/timing/events/ctaseq.py | 190 | 190 | 0% | +| slic/devices/timing/events/evr.py | 37 | 37 | 0% | +| slic/devices/timing/events/tma.py | 40 | 40 | 0% | +| slic/devices/timing/lasertiming.py | 253 | 184 | 27% | +| slic/devices/xdiagnostics/\_\_init\_\_.py | 2 | 0 | 100% | +| slic/devices/xdiagnostics/intensitymonitor.py | 124 | 92 | 26% | +| slic/devices/xdiagnostics/profilemonitor.py | 19 | 9 | 53% | +| slic/devices/xdiagnostics/timetools.py | 48 | 48 | 0% | +| slic/devices/xoptics/\_\_init\_\_.py | 7 | 0 | 100% | +| slic/devices/xoptics/aramis\_attenuator.py | 96 | 66 | 31% | +| slic/devices/xoptics/aramis\_reflaser.py | 23 | 15 | 35% | +| slic/devices/xoptics/dcm.py | 211 | 162 | 23% | +| slic/devices/xoptics/kb.py | 30 | 18 | 40% | +| slic/devices/xoptics/offsetmirrors.py | 9 | 5 | 44% | +| slic/devices/xoptics/pulsepicker.py | 56 | 34 | 39% | +| slic/devices/xoptics/slits/\_\_init\_\_.py | 5 | 0 | 100% | +| slic/devices/xoptics/slits/slitblades.py | 66 | 48 | 27% | +| slic/devices/xoptics/slits/slittwinunit.py | 12 | 6 | 50% | +| slic/devices/xoptics/slits/slitunit.py | 14 | 8 | 43% | +| slic/devices/xoptics/slits/slitunitcw.py | 7 | 3 | 57% | +| slic/devices/xoptics/slits/slitunitjj.py | 8 | 5 | 38% | +| slic/gui/\_\_init\_\_.py | 1 | 0 | 100% | +| slic/gui/daqframe.py | 75 | 54 | 28% | +| slic/gui/daqpanels/\_\_init\_\_.py | 6 | 0 | 100% | +| slic/gui/daqpanels/config.py | 98 | 80 | 18% | +| slic/gui/daqpanels/goto.py | 92 | 76 | 17% | +| slic/gui/daqpanels/run.py | 56 | 46 | 18% | +| slic/gui/daqpanels/scan2d.py | 77 | 66 | 14% | +| slic/gui/daqpanels/scan.py | 63 | 54 | 14% | +| slic/gui/daqpanels/sfx.py | 77 | 60 | 22% | +| slic/gui/daqpanels/special.py | 63 | 54 | 14% | +| slic/gui/daqpanels/static.py | 46 | 37 | 20% | +| slic/gui/daqpanels/tools.py | 140 | 114 | 19% | +| slic/gui/daqpanels/tweak.py | 149 | 127 | 15% | +| slic/gui/gui.py | 16 | 10 | 38% | +| slic/gui/icon.py | 8 | 4 | 50% | +| slic/gui/persist.py | 68 | 48 | 29% | +| slic/gui/widgets/\_\_init\_\_.py | 11 | 0 | 100% | +| slic/gui/widgets/alarm.py | 21 | 9 | 57% | +| slic/gui/widgets/alternative.py | 51 | 40 | 22% | +| slic/gui/widgets/boxes.py | 33 | 26 | 21% | +| slic/gui/widgets/checkbox.py | 8 | 4 | 50% | +| slic/gui/widgets/completers.py | 27 | 19 | 30% | +| slic/gui/widgets/dyncombo.py | 49 | 49 | 0% | +| slic/gui/widgets/entries.py | 253 | 195 | 23% | +| slic/gui/widgets/exc2warn.py | 15 | 13 | 13% | +| slic/gui/widgets/fname.py | 60 | 47 | 22% | +| slic/gui/widgets/jfcfg.py | 290 | 230 | 21% | +| slic/gui/widgets/jfmodcoords.py | 88 | 70 | 20% | +| slic/gui/widgets/labeled.py | 19 | 7 | 63% | +| slic/gui/widgets/lists.py | 96 | 73 | 24% | +| slic/gui/widgets/mods.py | 25 | 17 | 32% | +| slic/gui/widgets/nope.py | 26 | 19 | 27% | +| slic/gui/widgets/plotting.py | 68 | 47 | 31% | +| slic/gui/widgets/tools.py | 11 | 7 | 36% | +| slic/gui/widgets/twobuttons.py | 43 | 30 | 30% | +| slic/gui/wxdebug.py | 15 | 7 | 53% | +| slic/utils/\_\_init\_\_.py | 24 | 0 | 100% | +| slic/utils/argfwd.py | 53 | 14 | 74% | +| slic/utils/ask\_yes\_no.py | 27 | 20 | 26% | +| slic/utils/channels.py | 17 | 12 | 29% | +| slic/utils/config.py | 5 | 2 | 60% | +| slic/utils/cprint.py | 41 | 16 | 61% | +| slic/utils/dbusnotify.py | 40 | 24 | 40% | +| slic/utils/debug.py | 16 | 12 | 25% | +| slic/utils/dictext.py | 30 | 19 | 37% | +| slic/utils/dotdir.py | 10 | 1 | 90% | +| slic/utils/duo.py | 77 | 45 | 42% | +| slic/utils/elog.py | 32 | 0 | 100% | +| slic/utils/eval.py | 49 | 37 | 24% | +| slic/utils/exceptions.py | 22 | 14 | 36% | +| slic/utils/get\_adj.py | 17 | 11 | 35% | +| slic/utils/hastyepics.py | 37 | 25 | 32% | +| slic/utils/ioc/\_\_init\_\_.py | 1 | 1 | 0% | +| slic/utils/ioc/adjdrv.py | 31 | 31 | 0% | +| slic/utils/ioc/ioc.py | 63 | 63 | 0% | +| slic/utils/ipy.py | 22 | 15 | 32% | +| slic/utils/jsonext.py | 24 | 16 | 33% | +| slic/utils/lazypv.py | 12 | 12 | 0% | +| slic/utils/logbook.py | 442 | 274 | 38% | +| slic/utils/logbook\_exceptions.py | 13 | 0 | 100% | +| slic/utils/logcfg.py | 52 | 2 | 96% | +| slic/utils/logign.py | 22 | 14 | 36% | +| slic/utils/marker.py | 48 | 31 | 35% | +| slic/utils/metaclasses.py | 8 | 0 | 100% | +| slic/utils/namespace.py | 5 | 1 | 80% | +| slic/utils/npy.py | 71 | 56 | 21% | +| slic/utils/opmsg.py | 122 | 122 | 0% | +| slic/utils/path.py | 32 | 24 | 25% | +| slic/utils/picklio.py | 7 | 3 | 57% | +| slic/utils/printing.py | 77 | 61 | 21% | +| slic/utils/pv.py | 30 | 20 | 33% | +| slic/utils/pvpreload.py | 50 | 19 | 62% | +| slic/utils/pyepics.py | 64 | 39 | 39% | +| slic/utils/rangebar.py | 92 | 61 | 34% | +| slic/utils/readable.py | 12 | 9 | 25% | +| slic/utils/registry.py | 33 | 19 | 42% | +| slic/utils/reprate.py | 55 | 42 | 24% | +| slic/utils/richcfg.py | 21 | 11 | 48% | +| slic/utils/run\_later.py | 64 | 64 | 0% | +| slic/utils/screenshot.py | 30 | 19 | 37% | +| slic/utils/sendmail.py | 49 | 49 | 0% | +| slic/utils/sendsms.py | 5 | 5 | 0% | +| slic/utils/shortcut.py | 38 | 20 | 47% | +| slic/utils/snapshot.py | 6 | 3 | 50% | +| slic/utils/termtitle.py | 3 | 0 | 100% | +| slic/utils/tqdm\_mod.py | 28 | 18 | 36% | +| slic/utils/trinary.py | 4 | 2 | 50% | +| slic/utils/typecast.py | 19 | 19 | 0% | +| slic/utils/utils.py | 17 | 8 | 53% | +| slic/utils/xrange.py | 33 | 30 | 9% | +| **TOTAL** | **9985** | **7249** | **27%** | diff --git a/markdown/pytest-report.json b/markdown/pytest-report.json index 13c763514..bc7899e90 100644 --- a/markdown/pytest-report.json +++ b/markdown/pytest-report.json @@ -1 +1 @@ -{"created": 1755181959.4555445, "duration": 5.246391773223877, "exitcode": 1, "root": "/workspace/tligui_y/slic", "environment": {}, "summary": {"failed": 5, "total": 5, "collected": 5}, "collectors": [{"nodeid": "", "outcome": "passed", "result": [{"nodeid": "tests/test_utils_elog.py", "type": "Module"}]}, {"nodeid": "tests/test_utils_elog.py", "outcome": "passed", "result": [{"nodeid": "tests/test_utils_elog.py::test_get_default_elog_instance_with_direct_password_and_real_check", "type": "Function", "lineno": 12}, {"nodeid": "tests/test_utils_elog.py::test_get_default_elog_instance_with_wrong_password_and_real_check", "type": "Function", "lineno": 37}, {"nodeid": "tests/test_utils_elog.py::test_get_default_elog_instance_asks_password_and_opens", "type": "Function", "lineno": 46}, {"nodeid": "tests/test_utils_elog.py::test_get_default_elog_with_path_home", "type": "Function", "lineno": 72}, {"nodeid": "tests/test_utils_elog.py::test_screenshot", "type": "Function", "lineno": 121}]}], "tests": [{"nodeid": "tests/test_utils_elog.py::test_get_default_elog_instance_with_direct_password_and_real_check", "lineno": 12, "outcome": "failed", "keywords": ["test_get_default_elog_instance_with_direct_password_and_real_check", "test_utils_elog.py", "tests", "slic", ""], "setup": {"duration": 0.00035812100395560265, "outcome": "passed"}, "call": {"duration": 0.011955311987549067, "outcome": "failed", "crash": {"path": "/workspace/tligui_y/slic/tests/test_utils_elog.py", "lineno": 24, "message": "Failed: elog.open() raised an unexpected exception: Invalid username or password."}, "traceback": [{"path": "tests/test_utils_elog.py", "lineno": 24, "message": "Failed"}], "longrepr": "def test_get_default_elog_instance_with_direct_password_and_real_check():\n url = \"http://localhost:8080/demo\"\n user = \"robot\"\n password = \"testpassword\"\n text = \"This is a message1\"\n \n elog = Elog(\"http://localhost:8080/demo\", user=user, password=password, encrypt_pwd=False)\n \n try:\n> elog.post(text)\n\ntests/test_utils_elog.py:22: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \nslic/utils/elog.py:17: in post\n return self._log.post(*args, **kwargs)\nslic/utils/logbook.py:273: in post\n resp_message, resp_headers, resp_msg_id = _validate_response(response)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nresponse = \n\n def _validate_response(response):\n \"\"\" Validate response of the request.\"\"\"\n print(\"[DEBUG] _validate_response called\")\n print(f\" HTTP status code : {response.status_code}\")\n print(f\" Response headers : {response.headers}\")\n print(f\" First 300 bytes of content:\\n{response.content[:300]!r}\")\n \n msg_id = None\n \n if response.status_code not in [200, 302]:\n print(\"[DEBUG] Status not 200/302 \u2192 searching for \")\n err = re.findall('.*?',\n response.content.decode('utf-8', 'ignore'),\n flags=re.DOTALL)\n if len(err) > 0:\n err = re.sub('(?:<.*?>)', '', err[0])\n print(f\"[DEBUG] Found error message: {err}\")\n if err:\n raise LogbookMessageRejected('Rejected because of: ' + err)\n else:\n raise LogbookMessageRejected('Rejected because of unknown error.')\n raise LogbookMessageRejected('Rejected because of unknown error.')\n else:\n location = response.headers.get('Location')\n if location is not None:\n print(f\"[DEBUG] Location header: {location}\")\n if 'has moved' in location:\n raise LogbookServerProblem('Logbook server has moved to another location.')\n elif 'fail' in location:\n raise LogbookAuthenticationError('Invalid username or password.')\n else:\n try:\n msg_id = int(urllib.parse.urlsplit(location).path.split('/')[-1])\n print(f\"[DEBUG] Parsed msg_id from Location: {msg_id}\")\n except ValueError:\n print(\"[DEBUG] Could not parse msg_id from Location\")\n msg_id = None\n \n if b'type=password' in response.content or b'type=\"password\"' in response.content:\n print(\"[DEBUG] Password input detected in response HTML \u2014 assuming login page \u2192 AUTH ERROR\")\n> raise LogbookAuthenticationError('Invalid username or password.')\nE slic.utils.logbook_exceptions.LogbookAuthenticationError: Invalid username or password.\n\nslic/utils/logbook.py:833: LogbookAuthenticationError\n\nDuring handling of the above exception, another exception occurred:\n\n def test_get_default_elog_instance_with_direct_password_and_real_check():\n url = \"http://localhost:8080/demo\"\n user = \"robot\"\n password = \"testpassword\"\n text = \"This is a message1\"\n \n elog = Elog(\"http://localhost:8080/demo\", user=user, password=password, encrypt_pwd=False)\n \n try:\n elog.post(text)\n except Exception as e:\n> pytest.fail(f\"elog.open() raised an unexpected exception: {e}\")\nE Failed: elog.open() raised an unexpected exception: Invalid username or password.\n\ntests/test_utils_elog.py:24: Failed"}, "teardown": {"duration": 0.0003204937092959881, "outcome": "passed"}}, {"nodeid": "tests/test_utils_elog.py::test_get_default_elog_instance_with_wrong_password_and_real_check", "lineno": 37, "outcome": "failed", "keywords": ["test_get_default_elog_instance_with_wrong_password_and_real_check", "test_utils_elog.py", "tests", "slic", ""], "setup": {"duration": 0.00013607600703835487, "outcome": "passed"}, "call": {"duration": 0.00034243473783135414, "outcome": "failed", "crash": {"path": "/workspace/tligui_y/slic/tests/test_utils_elog.py", "lineno": 44, "message": "Failed: DID NOT RAISE "}, "traceback": [{"path": "tests/test_utils_elog.py", "lineno": 44, "message": "Failed"}], "longrepr": "def test_get_default_elog_instance_with_wrong_password_and_real_check():\n url = \"http://localhost:8080/demo\"\n user = \"robot\"\n wrong_password = \"wrongpassword\"\n \n with pytest.raises(LogbookAuthenticationError):\n> Elog(url, user=user, password=wrong_password, encrypt_pwd=False)\nE Failed: DID NOT RAISE \n\ntests/test_utils_elog.py:44: Failed"}, "teardown": {"duration": 0.00016059307381510735, "outcome": "passed"}}, {"nodeid": "tests/test_utils_elog.py::test_get_default_elog_instance_asks_password_and_opens", "lineno": 46, "outcome": "failed", "keywords": ["test_get_default_elog_instance_asks_password_and_opens", "__wrapped__", "patchings", "test_utils_elog.py", "tests", "slic", ""], "setup": {"duration": 0.00012293364852666855, "outcome": "passed"}, "call": {"duration": 0.004283982794731855, "outcome": "failed", "crash": {"path": "/workspace/tligui_y/slic/tests/test_utils_elog.py", "lineno": 61, "message": "Failed: elog.open() raised an unexpected exception: Invalid username or password."}, "traceback": [{"path": "tests/test_utils_elog.py", "lineno": 61, "message": "Failed"}], "longrepr": "mock_home = \nmock_getpass = \n\n @patch(\"slic.utils.elog.getpass\")\n @patch(\"slic.utils.elog.Path.home\")\n def test_get_default_elog_instance_asks_password_and_opens(mock_home, mock_getpass):\n mock_home.return_value = Path(\"/does/not/exist\") # Fausse home \u2192 lecture \u00e9choue\n mock_getpass.return_value = \"testpassword\"\n user = \"robot\"\n text = \"This is a message2\"\n url = \"http://localhost:8080/demo\"\n \n elog = Elog(\"http://localhost:8080/demo\", user=user, encrypt_pwd=False)\n \n try:\n> elog.post(text)\n\ntests/test_utils_elog.py:59: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \nslic/utils/elog.py:17: in post\n return self._log.post(*args, **kwargs)\nslic/utils/logbook.py:273: in post\n resp_message, resp_headers, resp_msg_id = _validate_response(response)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nresponse = \n\n def _validate_response(response):\n \"\"\" Validate response of the request.\"\"\"\n print(\"[DEBUG] _validate_response called\")\n print(f\" HTTP status code : {response.status_code}\")\n print(f\" Response headers : {response.headers}\")\n print(f\" First 300 bytes of content:\\n{response.content[:300]!r}\")\n \n msg_id = None\n \n if response.status_code not in [200, 302]:\n print(\"[DEBUG] Status not 200/302 \u2192 searching for \")\n err = re.findall('.*?',\n response.content.decode('utf-8', 'ignore'),\n flags=re.DOTALL)\n if len(err) > 0:\n err = re.sub('(?:<.*?>)', '', err[0])\n print(f\"[DEBUG] Found error message: {err}\")\n if err:\n raise LogbookMessageRejected('Rejected because of: ' + err)\n else:\n raise LogbookMessageRejected('Rejected because of unknown error.')\n raise LogbookMessageRejected('Rejected because of unknown error.')\n else:\n location = response.headers.get('Location')\n if location is not None:\n print(f\"[DEBUG] Location header: {location}\")\n if 'has moved' in location:\n raise LogbookServerProblem('Logbook server has moved to another location.')\n elif 'fail' in location:\n raise LogbookAuthenticationError('Invalid username or password.')\n else:\n try:\n msg_id = int(urllib.parse.urlsplit(location).path.split('/')[-1])\n print(f\"[DEBUG] Parsed msg_id from Location: {msg_id}\")\n except ValueError:\n print(\"[DEBUG] Could not parse msg_id from Location\")\n msg_id = None\n \n if b'type=password' in response.content or b'type=\"password\"' in response.content:\n print(\"[DEBUG] Password input detected in response HTML \u2014 assuming login page \u2192 AUTH ERROR\")\n> raise LogbookAuthenticationError('Invalid username or password.')\nE slic.utils.logbook_exceptions.LogbookAuthenticationError: Invalid username or password.\n\nslic/utils/logbook.py:833: LogbookAuthenticationError\n\nDuring handling of the above exception, another exception occurred:\n\nmock_home = \nmock_getpass = \n\n @patch(\"slic.utils.elog.getpass\")\n @patch(\"slic.utils.elog.Path.home\")\n def test_get_default_elog_instance_asks_password_and_opens(mock_home, mock_getpass):\n mock_home.return_value = Path(\"/does/not/exist\") # Fausse home \u2192 lecture \u00e9choue\n mock_getpass.return_value = \"testpassword\"\n user = \"robot\"\n text = \"This is a message2\"\n url = \"http://localhost:8080/demo\"\n \n elog = Elog(\"http://localhost:8080/demo\", user=user, encrypt_pwd=False)\n \n try:\n elog.post(text)\n except Exception as e:\n> pytest.fail(f\"elog.open() raised an unexpected exception: {e}\")\nE Failed: elog.open() raised an unexpected exception: Invalid username or password.\n\ntests/test_utils_elog.py:61: Failed"}, "teardown": {"duration": 0.0002163117751479149, "outcome": "passed"}}, {"nodeid": "tests/test_utils_elog.py::test_get_default_elog_with_path_home", "lineno": 72, "outcome": "failed", "keywords": ["test_get_default_elog_with_path_home", "__wrapped__", "patchings", "test_utils_elog.py", "tests", "slic", ""], "setup": {"duration": 0.00013341102749109268, "outcome": "passed"}, "call": {"duration": 0.00466856500133872, "outcome": "failed", "crash": {"path": "/workspace/tligui_y/slic/tests/test_utils_elog.py", "lineno": 95, "message": "Failed: elog.open() raised an unexpected exception: Invalid username or password."}, "traceback": [{"path": "tests/test_utils_elog.py", "lineno": 95, "message": "Failed"}], "longrepr": "mock_home = \nmock_getuser = \nmock_getpass = \n\n @patch(\"slic.utils.elog.getpass\")\n @patch(\"slic.utils.elog.getuser\")\n @patch(\"slic.utils.elog.Path.home\")\n def test_get_default_elog_with_path_home(mock_home, mock_getuser, mock_getpass):\n fake_user = \"robot\"\n fake_pw = \"testpassword\"\n mock_getuser.return_value = fake_user\n mock_getpass.return_value = fake_pw # fallback safety\n text = \"This is a message3\"\n url = \"http://localhost:8080/demo\"\n \n tmp_home = Path(\"/tmp/fake_home_for_robot\")\n tmp_home.mkdir(parents=True, exist_ok=True)\n pw_file = tmp_home / \".elog_psi\"\n pw_file.write_text(fake_pw)\n mock_home.return_value = tmp_home\n \n try:\n elog = Elog(\"http://localhost:8080/demo\", encrypt_pwd=False)\n try:\n> elog.post(text)\n\ntests/test_utils_elog.py:93: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \nslic/utils/elog.py:17: in post\n return self._log.post(*args, **kwargs)\nslic/utils/logbook.py:273: in post\n resp_message, resp_headers, resp_msg_id = _validate_response(response)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nresponse = \n\n def _validate_response(response):\n \"\"\" Validate response of the request.\"\"\"\n print(\"[DEBUG] _validate_response called\")\n print(f\" HTTP status code : {response.status_code}\")\n print(f\" Response headers : {response.headers}\")\n print(f\" First 300 bytes of content:\\n{response.content[:300]!r}\")\n \n msg_id = None\n \n if response.status_code not in [200, 302]:\n print(\"[DEBUG] Status not 200/302 \u2192 searching for \")\n err = re.findall('.*?',\n response.content.decode('utf-8', 'ignore'),\n flags=re.DOTALL)\n if len(err) > 0:\n err = re.sub('(?:<.*?>)', '', err[0])\n print(f\"[DEBUG] Found error message: {err}\")\n if err:\n raise LogbookMessageRejected('Rejected because of: ' + err)\n else:\n raise LogbookMessageRejected('Rejected because of unknown error.')\n raise LogbookMessageRejected('Rejected because of unknown error.')\n else:\n location = response.headers.get('Location')\n if location is not None:\n print(f\"[DEBUG] Location header: {location}\")\n if 'has moved' in location:\n raise LogbookServerProblem('Logbook server has moved to another location.')\n elif 'fail' in location:\n raise LogbookAuthenticationError('Invalid username or password.')\n else:\n try:\n msg_id = int(urllib.parse.urlsplit(location).path.split('/')[-1])\n print(f\"[DEBUG] Parsed msg_id from Location: {msg_id}\")\n except ValueError:\n print(\"[DEBUG] Could not parse msg_id from Location\")\n msg_id = None\n \n if b'type=password' in response.content or b'type=\"password\"' in response.content:\n print(\"[DEBUG] Password input detected in response HTML \u2014 assuming login page \u2192 AUTH ERROR\")\n> raise LogbookAuthenticationError('Invalid username or password.')\nE slic.utils.logbook_exceptions.LogbookAuthenticationError: Invalid username or password.\n\nslic/utils/logbook.py:833: LogbookAuthenticationError\n\nDuring handling of the above exception, another exception occurred:\n\nmock_home = \nmock_getuser = \nmock_getpass = \n\n @patch(\"slic.utils.elog.getpass\")\n @patch(\"slic.utils.elog.getuser\")\n @patch(\"slic.utils.elog.Path.home\")\n def test_get_default_elog_with_path_home(mock_home, mock_getuser, mock_getpass):\n fake_user = \"robot\"\n fake_pw = \"testpassword\"\n mock_getuser.return_value = fake_user\n mock_getpass.return_value = fake_pw # fallback safety\n text = \"This is a message3\"\n url = \"http://localhost:8080/demo\"\n \n tmp_home = Path(\"/tmp/fake_home_for_robot\")\n tmp_home.mkdir(parents=True, exist_ok=True)\n pw_file = tmp_home / \".elog_psi\"\n pw_file.write_text(fake_pw)\n mock_home.return_value = tmp_home\n \n try:\n elog = Elog(\"http://localhost:8080/demo\", encrypt_pwd=False)\n try:\n elog.post(text)\n except Exception as e:\n> pytest.fail(f\"elog.open() raised an unexpected exception: {e}\")\nE Failed: elog.open() raised an unexpected exception: Invalid username or password.\n\ntests/test_utils_elog.py:95: Failed"}, "teardown": {"duration": 0.0002215951681137085, "outcome": "passed"}}, {"nodeid": "tests/test_utils_elog.py::test_screenshot", "lineno": 121, "outcome": "failed", "keywords": ["test_screenshot", "__wrapped__", "patchings", "test_utils_elog.py", "tests", "slic", ""], "setup": {"duration": 0.00013715913519263268, "outcome": "passed"}, "call": {"duration": 0.004666307009756565, "outcome": "failed", "crash": {"path": "/workspace/tligui_y/slic/slic/utils/logbook.py", "lineno": 833, "message": "slic.utils.logbook_exceptions.LogbookAuthenticationError: Invalid username or password."}, "traceback": [{"path": "tests/test_utils_elog.py", "lineno": 134, "message": ""}, {"path": "slic/utils/elog.py", "lineno": 22, "message": "in screenshot"}, {"path": "slic/utils/elog.py", "lineno": 17, "message": "in post"}, {"path": "slic/utils/logbook.py", "lineno": 273, "message": "in post"}, {"path": "slic/utils/logbook.py", "lineno": 833, "message": "LogbookAuthenticationError"}], "longrepr": "mock_screenshot_class = \n\n @patch(\"slic.utils.elog.Screenshot\")\n def test_screenshot(mock_screenshot_class):\n fake_path = \"/tmp/fake_screenshot.png\"\n with open(fake_path, \"wb\") as f:\n f.write(b\"fake image data\")\n \n mock_instance = mock_screenshot_class.return_value\n mock_instance.shoot.return_value = [fake_path]\n \n elog = elog = Elog(\"http://localhost:8080/demo\", user=\"robot\", password=\"testpassword\", encrypt_pwd=False)\n \n test_msg = \"SCREENSHOT_INTEGRATION_TEST_MSG_456\"\n> elog.screenshot(message=test_msg)\n\ntests/test_utils_elog.py:134: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \nslic/utils/elog.py:22: in screenshot\n self.post(message, **kwargs)\nslic/utils/elog.py:17: in post\n return self._log.post(*args, **kwargs)\nslic/utils/logbook.py:273: in post\n resp_message, resp_headers, resp_msg_id = _validate_response(response)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nresponse = \n\n def _validate_response(response):\n \"\"\" Validate response of the request.\"\"\"\n print(\"[DEBUG] _validate_response called\")\n print(f\" HTTP status code : {response.status_code}\")\n print(f\" Response headers : {response.headers}\")\n print(f\" First 300 bytes of content:\\n{response.content[:300]!r}\")\n \n msg_id = None\n \n if response.status_code not in [200, 302]:\n print(\"[DEBUG] Status not 200/302 \u2192 searching for \")\n err = re.findall('.*?',\n response.content.decode('utf-8', 'ignore'),\n flags=re.DOTALL)\n if len(err) > 0:\n err = re.sub('(?:<.*?>)', '', err[0])\n print(f\"[DEBUG] Found error message: {err}\")\n if err:\n raise LogbookMessageRejected('Rejected because of: ' + err)\n else:\n raise LogbookMessageRejected('Rejected because of unknown error.')\n raise LogbookMessageRejected('Rejected because of unknown error.')\n else:\n location = response.headers.get('Location')\n if location is not None:\n print(f\"[DEBUG] Location header: {location}\")\n if 'has moved' in location:\n raise LogbookServerProblem('Logbook server has moved to another location.')\n elif 'fail' in location:\n raise LogbookAuthenticationError('Invalid username or password.')\n else:\n try:\n msg_id = int(urllib.parse.urlsplit(location).path.split('/')[-1])\n print(f\"[DEBUG] Parsed msg_id from Location: {msg_id}\")\n except ValueError:\n print(\"[DEBUG] Could not parse msg_id from Location\")\n msg_id = None\n \n if b'type=password' in response.content or b'type=\"password\"' in response.content:\n print(\"[DEBUG] Password input detected in response HTML \u2014 assuming login page \u2192 AUTH ERROR\")\n> raise LogbookAuthenticationError('Invalid username or password.')\nE slic.utils.logbook_exceptions.LogbookAuthenticationError: Invalid username or password.\n\nslic/utils/logbook.py:833: LogbookAuthenticationError"}, "teardown": {"duration": 0.0002205907367169857, "outcome": "passed"}}], "warnings": [{"message": "invalid escape sequence \\-", "category": "DeprecationWarning", "when": "collect", "filename": "/workspace/tligui_y/slic/.pixi/envs/default/lib/python3.8/site-packages/bsread/h5.py", "lineno": 207}, {"message": "The module numpy.dual is deprecated. Instead of using dual, use the functions directly from numpy or scipy.", "category": "DeprecationWarning", "when": "collect", "filename": "/workspace/tligui_y/slic/.pixi/envs/default/lib/python3.8/site-packages/scipy/fft/__init__.py", "lineno": 97}]} \ No newline at end of file +{"created": 1755182073.1052196, "duration": 6.748669385910034, "exitcode": 1, "root": "/workspace/tligui_y/slic", "environment": {}, "summary": {"failed": 5, "total": 5, "collected": 5}, "collectors": [{"nodeid": "", "outcome": "passed", "result": [{"nodeid": "tests/test_utils_elog.py", "type": "Module"}]}, {"nodeid": "tests/test_utils_elog.py", "outcome": "passed", "result": [{"nodeid": "tests/test_utils_elog.py::test_get_default_elog_instance_with_direct_password_and_real_check", "type": "Function", "lineno": 12}, {"nodeid": "tests/test_utils_elog.py::test_get_default_elog_instance_with_wrong_password_and_real_check", "type": "Function", "lineno": 37}, {"nodeid": "tests/test_utils_elog.py::test_get_default_elog_instance_asks_password_and_opens", "type": "Function", "lineno": 46}, {"nodeid": "tests/test_utils_elog.py::test_get_default_elog_with_path_home", "type": "Function", "lineno": 72}, {"nodeid": "tests/test_utils_elog.py::test_screenshot", "type": "Function", "lineno": 121}]}], "tests": [{"nodeid": "tests/test_utils_elog.py::test_get_default_elog_instance_with_direct_password_and_real_check", "lineno": 12, "outcome": "failed", "keywords": ["test_get_default_elog_instance_with_direct_password_and_real_check", "test_utils_elog.py", "tests", "slic", ""], "setup": {"duration": 0.00038035912439227104, "outcome": "passed"}, "call": {"duration": 0.005957710091024637, "outcome": "failed", "crash": {"path": "/workspace/tligui_y/slic/tests/test_utils_elog.py", "lineno": 24, "message": "Failed: elog.open() raised an unexpected exception: No response from the logbook server.\nDetails: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))"}, "traceback": [{"path": "tests/test_utils_elog.py", "lineno": 24, "message": "Failed"}], "longrepr": "self = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n> sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection\n raise err\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\naddress = ('localhost', 8080), timeout = None, source_address = None\nsocket_options = [(6, 1, 1)]\n\n def create_connection(\n address: tuple[str, int],\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n source_address: tuple[str, int] | None = None,\n socket_options: _TYPE_SOCKET_OPTIONS | None = None,\n ) -> socket.socket:\n \"\"\"Connect to *address* and return the socket object.\n \n Convenience function. Connect to *address* (a 2-tuple ``(host,\n port)``) and return the socket object. Passing the optional\n *timeout* parameter will set the timeout on the socket instance\n before attempting to connect. If no *timeout* is supplied, the\n global default timeout setting returned by :func:`socket.getdefaulttimeout`\n is used. If *source_address* is set it must be a tuple of (host, port)\n for the socket to bind as a source address before making the connection.\n An host of '' or port 0 tells the OS to use the default.\n \"\"\"\n \n host, port = address\n if host.startswith(\"[\"):\n host = host.strip(\"[]\")\n err = None\n \n # Using the value from allowed_gai_family() in the context of getaddrinfo lets\n # us select whether to work with IPv4 DNS records, IPv6 records, or both.\n # The original create_connection function always returns all records.\n family = allowed_gai_family()\n \n try:\n host.encode(\"idna\")\n except UnicodeError:\n raise LocationParseError(f\"'{host}', label empty or too long\") from None\n \n for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):\n af, socktype, proto, canonname, sa = res\n sock = None\n try:\n sock = socket.socket(af, socktype, proto)\n \n # If provided, set socket level options before connecting.\n _set_socket_options(sock, socket_options)\n \n if timeout is not _DEFAULT_TIMEOUT:\n sock.settimeout(timeout)\n if source_address:\n sock.bind(source_address)\n> sock.connect(sa)\nE ConnectionRefusedError: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nmethod = 'POST', url = '/demo/'\nbody = b'--076f6dcb4da3b3dc1982af7cab425820\\r\\nContent-Disposition: form-data; name=\"Author\"\\r\\n\\r\\nrobot\\r\\n--076f6dcb4da3b3...Disposition: form-data; name=\"Text\"; filename=\"\"\\r\\n\\r\\nThis is a message1\\r\\n--076f6dcb4da3b3dc1982af7cab425820--\\r\\n'\nheaders = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '705', 'Content-Type': 'multipart/form-data; boundary=076f6dcb4da3b3dc1982af7cab425820'}\nretries = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nredirect = False, assert_same_host = False\ntimeout = Timeout(connect=None, read=None, total=None), pool_timeout = None\nrelease_conn = False, chunked = False, body_pos = None, preload_content = False\ndecode_content = False, response_kw = {}\nparsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/', query=None, fragment=None)\ndestination_scheme = None, conn = None, release_this_conn = True\nhttp_tunnel_required = False, err = None, clean_exit = False\n\n def urlopen( # type: ignore[override]\n self,\n method: str,\n url: str,\n body: _TYPE_BODY | None = None,\n headers: typing.Mapping[str, str] | None = None,\n retries: Retry | bool | int | None = None,\n redirect: bool = True,\n assert_same_host: bool = True,\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n pool_timeout: int | None = None,\n release_conn: bool | None = None,\n chunked: bool = False,\n body_pos: _TYPE_BODY_POSITION | None = None,\n preload_content: bool = True,\n decode_content: bool = True,\n **response_kw: typing.Any,\n ) -> BaseHTTPResponse:\n \"\"\"\n Get a connection from the pool and perform an HTTP request. This is the\n lowest level call for making a request, so you'll need to specify all\n the raw details.\n \n .. note::\n \n More commonly, it's appropriate to use a convenience method\n such as :meth:`request`.\n \n .. note::\n \n `release_conn` will only behave as expected if\n `preload_content=False` because we want to make\n `preload_content=False` the default behaviour someday soon without\n breaking backwards compatibility.\n \n :param method:\n HTTP request method (such as GET, POST, PUT, etc.)\n \n :param url:\n The URL to perform the request on.\n \n :param body:\n Data to send in the request body, either :class:`str`, :class:`bytes`,\n an iterable of :class:`str`/:class:`bytes`, or a file-like object.\n \n :param headers:\n Dictionary of custom headers to send, such as User-Agent,\n If-None-Match, etc. If None, pool headers are used. If provided,\n these headers completely replace any pool-specific headers.\n \n :param retries:\n Configure the number of retries to allow before raising a\n :class:`~urllib3.exceptions.MaxRetryError` exception.\n \n If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a\n :class:`~urllib3.util.retry.Retry` object for fine-grained control\n over different types of retries.\n Pass an integer number to retry connection errors that many times,\n but no other types of errors. Pass zero to never retry.\n \n If ``False``, then retries are disabled and any exception is raised\n immediately. Also, instead of raising a MaxRetryError on redirects,\n the redirect response will be returned.\n \n :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int.\n \n :param redirect:\n If True, automatically handle redirects (status codes 301, 302,\n 303, 307, 308). Each redirect counts as a retry. Disabling retries\n will disable redirect, too.\n \n :param assert_same_host:\n If ``True``, will make sure that the host of the pool requests is\n consistent else will raise HostChangedError. When ``False``, you can\n use the pool on an HTTP proxy and request foreign hosts.\n \n :param timeout:\n If specified, overrides the default timeout for this one\n request. It may be a float (in seconds) or an instance of\n :class:`urllib3.util.Timeout`.\n \n :param pool_timeout:\n If set and the pool is set to block=True, then this method will\n block for ``pool_timeout`` seconds and raise EmptyPoolError if no\n connection is available within the time period.\n \n :param bool preload_content:\n If True, the response's body will be preloaded into memory.\n \n :param bool decode_content:\n If True, will attempt to decode the body based on the\n 'content-encoding' header.\n \n :param release_conn:\n If False, then the urlopen call will not release the connection\n back into the pool once a response is received (but will release if\n you read the entire contents of the response such as when\n `preload_content=True`). This is useful if you're not preloading\n the response's content immediately. You will need to call\n ``r.release_conn()`` on the response ``r`` to return the connection\n back into the pool. If None, it takes the value of ``preload_content``\n which defaults to ``True``.\n \n :param bool chunked:\n If True, urllib3 will send the body using chunked transfer\n encoding. Otherwise, urllib3 will send the body using the standard\n content-length form. Defaults to False.\n \n :param int body_pos:\n Position to seek to in file-like body in the event of a retry or\n redirect. Typically this won't need to be set because urllib3 will\n auto-populate the value when needed.\n \"\"\"\n parsed_url = parse_url(url)\n destination_scheme = parsed_url.scheme\n \n if headers is None:\n headers = self.headers\n \n if not isinstance(retries, Retry):\n retries = Retry.from_int(retries, redirect=redirect, default=self.retries)\n \n if release_conn is None:\n release_conn = preload_content\n \n # Check host\n if assert_same_host and not self.is_same_host(url):\n raise HostChangedError(self, url, retries)\n \n # Ensure that the URL we're connecting to is properly encoded\n if url.startswith(\"/\"):\n url = to_str(_encode_target(url))\n else:\n url = to_str(parsed_url.url)\n \n conn = None\n \n # Track whether `conn` needs to be released before\n # returning/raising/recursing. Update this variable if necessary, and\n # leave `release_conn` constant throughout the function. That way, if\n # the function recurses, the original value of `release_conn` will be\n # passed down into the recursive call, and its value will be respected.\n #\n # See issue #651 [1] for details.\n #\n # [1] \n release_this_conn = release_conn\n \n http_tunnel_required = connection_requires_http_tunnel(\n self.proxy, self.proxy_config, destination_scheme\n )\n \n # Merge the proxy headers. Only done when not using HTTP CONNECT. We\n # have to copy the headers dict so we can safely change it without those\n # changes being reflected in anyone else's copy.\n if not http_tunnel_required:\n headers = headers.copy() # type: ignore[attr-defined]\n headers.update(self.proxy_headers) # type: ignore[union-attr]\n \n # Must keep the exception bound to a separate variable or else Python 3\n # complains about UnboundLocalError.\n err = None\n \n # Keep track of whether we cleanly exited the except block. This\n # ensures we do proper cleanup in finally.\n clean_exit = False\n \n # Rewind body position, if needed. Record current position\n # for future rewinds in the event of a redirect/retry.\n body_pos = set_file_position(body, body_pos)\n \n try:\n # Request a connection from the queue.\n timeout_obj = self._get_timeout(timeout)\n conn = self._get_conn(timeout=pool_timeout)\n \n conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment]\n \n # Is this a closed/new connection that requires CONNECT tunnelling?\n if self.proxy is not None and http_tunnel_required and conn.is_closed:\n try:\n self._prepare_proxy(conn)\n except (BaseSSLError, OSError, SocketTimeout) as e:\n self._raise_timeout(\n err=e, url=self.proxy.url, timeout_value=conn.timeout\n )\n raise\n \n # If we're going to release the connection in ``finally:``, then\n # the response doesn't need to know about the connection. Otherwise\n # it will also try to release it and we'll have a double-release\n # mess.\n response_conn = conn if not release_conn else None\n \n # Make the request on the HTTPConnection object\n> response = self._make_request(\n conn,\n method,\n url,\n timeout=timeout_obj,\n body=body,\n headers=headers,\n chunked=chunked,\n retries=retries,\n response_conn=response_conn,\n preload_content=preload_content,\n decode_content=decode_content,\n **response_kw,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request\n conn.request(\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request\n self.endheaders()\n.pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders\n self._send_output(message_body, encode_chunked=encode_chunked)\n.pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output\n self.send(msg)\n.pixi/envs/default/lib/python3.8/http/client.py:951: in send\n self.connect()\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect\n self.sock = self._new_conn()\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n except socket.gaierror as e:\n raise NameResolutionError(self.host, self, e) from e\n except SocketTimeout as e:\n raise ConnectTimeoutError(\n self,\n f\"Connection to {self.host} timed out. (connect timeout={self.timeout})\",\n ) from e\n \n except OSError as e:\n> raise NewConnectionError(\n self, f\"Failed to establish a new connection: {e}\"\n ) from e\nE urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n> resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen\n retries = retries.increment(\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nmethod = 'POST', url = '/demo/', response = None\nerror = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')\n_pool = \n_stacktrace = \n\n def increment(\n self,\n method: str | None = None,\n url: str | None = None,\n response: BaseHTTPResponse | None = None,\n error: Exception | None = None,\n _pool: ConnectionPool | None = None,\n _stacktrace: TracebackType | None = None,\n ) -> Self:\n \"\"\"Return a new Retry object with incremented retry counters.\n \n :param response: A response object, or None, if the server did not\n return a response.\n :type response: :class:`~urllib3.response.BaseHTTPResponse`\n :param Exception error: An error encountered during the request, or\n None if the response was received successfully.\n \n :return: A new ``Retry`` object.\n \"\"\"\n if self.total is False and error:\n # Disabled, indicate to re-raise the error.\n raise reraise(type(error), error, _stacktrace)\n \n total = self.total\n if total is not None:\n total -= 1\n \n connect = self.connect\n read = self.read\n redirect = self.redirect\n status_count = self.status\n other = self.other\n cause = \"unknown\"\n status = None\n redirect_location = None\n \n if error and self._is_connection_error(error):\n # Connect retry?\n if connect is False:\n raise reraise(type(error), error, _stacktrace)\n elif connect is not None:\n connect -= 1\n \n elif error and self._is_read_error(error):\n # Read retry?\n if read is False or method is None or not self._is_method_retryable(method):\n raise reraise(type(error), error, _stacktrace)\n elif read is not None:\n read -= 1\n \n elif error:\n # Other retry?\n if other is not None:\n other -= 1\n \n elif response and response.get_redirect_location():\n # Redirect retry?\n if redirect is not None:\n redirect -= 1\n cause = \"too many redirects\"\n response_redirect_location = response.get_redirect_location()\n if response_redirect_location:\n redirect_location = response_redirect_location\n status = response.status\n \n else:\n # Incrementing because of a server error like a 500 in\n # status_forcelist and the given method is in the allowed_methods\n cause = ResponseError.GENERIC_ERROR\n if response and response.status:\n if status_count is not None:\n status_count -= 1\n cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status)\n status = response.status\n \n history = self.history + (\n RequestHistory(method, url, error, status, redirect_location),\n )\n \n new_retry = self.new(\n total=total,\n connect=connect,\n read=read,\n redirect=redirect,\n status=status_count,\n other=other,\n history=history,\n )\n \n if new_retry.is_exhausted():\n reason = error or ResponseError(cause)\n> raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]\nE urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError\n\nDuring handling of the above exception, another exception occurred:\n\nself = \nmessage = 'This is a message1', msg_id = None, reply = False\nattributes = {'Author': 'robot', 'When': 1755182070, 'cmd': 'Submit', 'exp': 'demo', ...}\nattachments = [], suppress_email_notification = False, encoding = None\ntimeout = None, kwargs = {'Author': 'robot'}\nlogbook_directory = 'elog_instance/logbooks/demo'\nnew_attachment_list = [('Text', ('', b'This is a message1'))]\nobjects_to_close = []\nattributes_to_edit = {'Author': b'robot', 'When': 1755182070, 'cmd': b'Submit', 'exp': b'demo', ...}\n\n def post(self, message, msg_id=None, reply=False, attributes=None, attachments=None,\n suppress_email_notification=False, encoding=None, timeout=None, **kwargs):\n \"\"\"\n Posts message to the logbook. If msg_id is not specified new message will be created, otherwise existing\n message will be edited, or a reply (if reply=True) to it will be created. This method returns the msg_id\n of the newly created message.\n \"\"\"\n \n logbook_directory = \"elog_instance/logbooks/demo\"\n print(f\"Checking the existence of the directory {logbook_directory}\")\n \n # Check if the directory exists\n if not os.path.exists(logbook_directory):\n print(f\"The directory {logbook_directory} does not exist.\")\n else:\n print(f\"The directory {logbook_directory} exists.\")\n \n # Check write permissions\n if os.access(logbook_directory, os.W_OK):\n print(f\"The directory {logbook_directory} has write permissions.\")\n else:\n print(f\"The directory {logbook_directory} does not have write permissions.\")\n \n \n print(\"STARTING POST\")\n # Ajout des impressions pour d\u00e9boguer\n print(f\"Message to post: {message}\")\n print(f\"msg_id: {msg_id}\")\n print(f\"Attributes: {attributes}\")\n print(f\"Attachments: {attachments}\")\n print(f\"Encoding: {encoding}\")\n print(f\"Timeout: {timeout}\")\n print(f\"Additional kwargs: {kwargs}\")\n \n attributes = attributes or {}\n attributes = {**attributes, **kwargs} # kwargs as attributes with higher priority\n print(f\"Updated attributes: {attributes}\")\n \n attachments = attachments or []\n print(f\"Attachments list: {attachments}\")\n \n if encoding is not None:\n if encoding not in ['plain', 'HTML', 'ELCode']:\n raise LogbookMessageRejected('Invalid message encoding. Valid options: plain, HTML, ELCode.')\n attributes['Encoding'] = encoding\n \n if suppress_email_notification:\n attributes[\"suppress\"] = 1\n \n # Prepare attachments\n if attachments:\n new_attachment_list, objects_to_close = self._prepare_attachments(attachments)\n print(f\"New attachments prepared: {new_attachment_list}\")\n else:\n objects_to_close = []\n new_attachment_list = []\n \n attributes_to_edit = dict()\n \n if msg_id:\n print(f\"Editing message with msg_id: {msg_id}\")\n if reply:\n print(f\"Replying to message with msg_id: {msg_id}\")\n self._check_if_message_on_server(msg_id)\n attributes['reply_to'] = str(msg_id)\n else:\n print(\"Editing existing message.\")\n attributes['edit_id'] = str(msg_id)\n attributes['skiplock'] = '1'\n msg_to_edit, attributes_to_edit, existing_attachments_list = self.read(msg_id)\n \n # Merge new attributes\n for attribute, data in attributes.items():\n if data is not None:\n attributes_to_edit[attribute] = data\n \n print(f\"Attributes after merging: {attributes_to_edit}\")\n \n # Process existing attachments\n i = 0\n existing_attachments_filename_list = []\n for attachment in existing_attachments_list:\n attributes_to_edit[f'attachment{i}'] = os.path.basename(attachment)\n existing_attachments_filename_list.append(os.path.basename(attachment)[14:])\n i += 1\n \n print(f\"Existing attachments: {existing_attachments_filename_list}\")\n \n duplicate_attachment_list = []\n for new_attachment in new_attachment_list:\n new_attachment_filename = new_attachment[1][0]\n print(f\"Checking new attachment: {new_attachment_filename}\")\n if new_attachment_filename in existing_attachments_filename_list:\n # Same attachment exists on the server, compare content\n new_attachment_content = new_attachment[1][1].read()\n new_attachment[1][1].seek(0)\n attachment_index = existing_attachments_filename_list.index(new_attachment_filename)\n existing_attachment_content = self.download_attachment(\n url=existing_attachments_list[attachment_index],\n timeout=timeout\n )\n if new_attachment_content == existing_attachment_content:\n print(f\"Duplicate attachment detected: {new_attachment_filename}\")\n duplicate_attachment_list.append(new_attachment)\n else:\n print(f\"Attachment content has changed: {new_attachment_filename}\")\n self.delete_attachment(msg_id, attributes=attributes_to_edit,\n attachment_id=attachment_index,\n timeout=timeout, text=msg_to_edit)\n existing_attachments_filename_list.pop(attachment_index)\n existing_attachments_list.pop(attachment_index)\n \n print(f\"Duplicate attachments to remove: {duplicate_attachment_list}\")\n \n # Remove duplicates\n for attach in duplicate_attachment_list:\n new_attachment_list.remove(attach)\n \n print(f\"Final new attachments list: {new_attachment_list}\")\n else:\n # Creating a new message, add timestamp if not present\n if 'When' not in attributes:\n attributes['When'] = int(datetime.now().timestamp())\n \n # Final check on attributes\n if not attributes_to_edit:\n attributes_to_edit = attributes\n \n print(f\"Final attributes to send to the server: {attributes_to_edit}\")\n \n # Remove reserved attributes\n _remove_reserved_attributes(attributes_to_edit)\n \n new_attachment_list.append(('Text', ('', message.encode('iso-8859-1'))))\n print(f\"Final attachment list including message text: {new_attachment_list}\")\n \n # Add base message attributes\n self._add_base_msg_attributes(attributes_to_edit)\n print(f\"Attributes with base message added: {attributes_to_edit}\")\n \n # Sanitize attribute keys\n attributes_to_edit = _replace_special_characters_in_attribute_keys(attributes_to_edit)\n print(f\"Attributes after sanitizing keys: {attributes_to_edit}\")\n \n # Encode all string values in latin1\n attributes_to_edit = _encode_values(attributes_to_edit)\n print(f\"Attributes after encoding: {attributes_to_edit}\")\n \n try:\n print(\"Sending POST request to the server...\")\n> response = requests.post(self._url, data=attributes_to_edit, files=new_attachment_list,\n allow_redirects=False, verify=False, timeout=timeout)\n\nslic/utils/logbook.py:269: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:115: in post\n return request(\"post\", url, data=data, json=json, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request\n return session.request(method=method, url=url, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request\n resp = self.send(prep, **send_kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send\n r = adapter.send(request, **kwargs)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n \n except (ProtocolError, OSError) as err:\n raise ConnectionError(err, request=request)\n \n except MaxRetryError as e:\n if isinstance(e.reason, ConnectTimeoutError):\n # TODO: Remove this in 3.0.0: see #2811\n if not isinstance(e.reason, NewConnectionError):\n raise ConnectTimeout(e, request=request)\n \n if isinstance(e.reason, ResponseError):\n raise RetryError(e, request=request)\n \n if isinstance(e.reason, _ProxyError):\n raise ProxyError(e, request=request)\n \n if isinstance(e.reason, _SSLError):\n # This branch is for urllib3 v1.22 and later.\n raise SSLError(e, request=request)\n \n> raise ConnectionError(e, request=request)\nE requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError\n\nDuring handling of the above exception, another exception occurred:\n\nself = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n> sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection\n raise err\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\naddress = ('localhost', 8080), timeout = None, source_address = None\nsocket_options = [(6, 1, 1)]\n\n def create_connection(\n address: tuple[str, int],\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n source_address: tuple[str, int] | None = None,\n socket_options: _TYPE_SOCKET_OPTIONS | None = None,\n ) -> socket.socket:\n \"\"\"Connect to *address* and return the socket object.\n \n Convenience function. Connect to *address* (a 2-tuple ``(host,\n port)``) and return the socket object. Passing the optional\n *timeout* parameter will set the timeout on the socket instance\n before attempting to connect. If no *timeout* is supplied, the\n global default timeout setting returned by :func:`socket.getdefaulttimeout`\n is used. If *source_address* is set it must be a tuple of (host, port)\n for the socket to bind as a source address before making the connection.\n An host of '' or port 0 tells the OS to use the default.\n \"\"\"\n \n host, port = address\n if host.startswith(\"[\"):\n host = host.strip(\"[]\")\n err = None\n \n # Using the value from allowed_gai_family() in the context of getaddrinfo lets\n # us select whether to work with IPv4 DNS records, IPv6 records, or both.\n # The original create_connection function always returns all records.\n family = allowed_gai_family()\n \n try:\n host.encode(\"idna\")\n except UnicodeError:\n raise LocationParseError(f\"'{host}', label empty or too long\") from None\n \n for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):\n af, socktype, proto, canonname, sa = res\n sock = None\n try:\n sock = socket.socket(af, socktype, proto)\n \n # If provided, set socket level options before connecting.\n _set_socket_options(sock, socket_options)\n \n if timeout is not _DEFAULT_TIMEOUT:\n sock.settimeout(timeout)\n if source_address:\n sock.bind(source_address)\n> sock.connect(sa)\nE ConnectionRefusedError: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nmethod = 'GET', url = '/demo/None', body = None\nheaders = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'unm=robot;upwd=testpassword;'}\nretries = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nredirect = False, assert_same_host = False\ntimeout = Timeout(connect=None, read=None, total=None), pool_timeout = None\nrelease_conn = False, chunked = False, body_pos = None, preload_content = False\ndecode_content = False, response_kw = {}\nparsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/None', query=None, fragment=None)\ndestination_scheme = None, conn = None, release_this_conn = True\nhttp_tunnel_required = False, err = None, clean_exit = False\n\n def urlopen( # type: ignore[override]\n self,\n method: str,\n url: str,\n body: _TYPE_BODY | None = None,\n headers: typing.Mapping[str, str] | None = None,\n retries: Retry | bool | int | None = None,\n redirect: bool = True,\n assert_same_host: bool = True,\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n pool_timeout: int | None = None,\n release_conn: bool | None = None,\n chunked: bool = False,\n body_pos: _TYPE_BODY_POSITION | None = None,\n preload_content: bool = True,\n decode_content: bool = True,\n **response_kw: typing.Any,\n ) -> BaseHTTPResponse:\n \"\"\"\n Get a connection from the pool and perform an HTTP request. This is the\n lowest level call for making a request, so you'll need to specify all\n the raw details.\n \n .. note::\n \n More commonly, it's appropriate to use a convenience method\n such as :meth:`request`.\n \n .. note::\n \n `release_conn` will only behave as expected if\n `preload_content=False` because we want to make\n `preload_content=False` the default behaviour someday soon without\n breaking backwards compatibility.\n \n :param method:\n HTTP request method (such as GET, POST, PUT, etc.)\n \n :param url:\n The URL to perform the request on.\n \n :param body:\n Data to send in the request body, either :class:`str`, :class:`bytes`,\n an iterable of :class:`str`/:class:`bytes`, or a file-like object.\n \n :param headers:\n Dictionary of custom headers to send, such as User-Agent,\n If-None-Match, etc. If None, pool headers are used. If provided,\n these headers completely replace any pool-specific headers.\n \n :param retries:\n Configure the number of retries to allow before raising a\n :class:`~urllib3.exceptions.MaxRetryError` exception.\n \n If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a\n :class:`~urllib3.util.retry.Retry` object for fine-grained control\n over different types of retries.\n Pass an integer number to retry connection errors that many times,\n but no other types of errors. Pass zero to never retry.\n \n If ``False``, then retries are disabled and any exception is raised\n immediately. Also, instead of raising a MaxRetryError on redirects,\n the redirect response will be returned.\n \n :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int.\n \n :param redirect:\n If True, automatically handle redirects (status codes 301, 302,\n 303, 307, 308). Each redirect counts as a retry. Disabling retries\n will disable redirect, too.\n \n :param assert_same_host:\n If ``True``, will make sure that the host of the pool requests is\n consistent else will raise HostChangedError. When ``False``, you can\n use the pool on an HTTP proxy and request foreign hosts.\n \n :param timeout:\n If specified, overrides the default timeout for this one\n request. It may be a float (in seconds) or an instance of\n :class:`urllib3.util.Timeout`.\n \n :param pool_timeout:\n If set and the pool is set to block=True, then this method will\n block for ``pool_timeout`` seconds and raise EmptyPoolError if no\n connection is available within the time period.\n \n :param bool preload_content:\n If True, the response's body will be preloaded into memory.\n \n :param bool decode_content:\n If True, will attempt to decode the body based on the\n 'content-encoding' header.\n \n :param release_conn:\n If False, then the urlopen call will not release the connection\n back into the pool once a response is received (but will release if\n you read the entire contents of the response such as when\n `preload_content=True`). This is useful if you're not preloading\n the response's content immediately. You will need to call\n ``r.release_conn()`` on the response ``r`` to return the connection\n back into the pool. If None, it takes the value of ``preload_content``\n which defaults to ``True``.\n \n :param bool chunked:\n If True, urllib3 will send the body using chunked transfer\n encoding. Otherwise, urllib3 will send the body using the standard\n content-length form. Defaults to False.\n \n :param int body_pos:\n Position to seek to in file-like body in the event of a retry or\n redirect. Typically this won't need to be set because urllib3 will\n auto-populate the value when needed.\n \"\"\"\n parsed_url = parse_url(url)\n destination_scheme = parsed_url.scheme\n \n if headers is None:\n headers = self.headers\n \n if not isinstance(retries, Retry):\n retries = Retry.from_int(retries, redirect=redirect, default=self.retries)\n \n if release_conn is None:\n release_conn = preload_content\n \n # Check host\n if assert_same_host and not self.is_same_host(url):\n raise HostChangedError(self, url, retries)\n \n # Ensure that the URL we're connecting to is properly encoded\n if url.startswith(\"/\"):\n url = to_str(_encode_target(url))\n else:\n url = to_str(parsed_url.url)\n \n conn = None\n \n # Track whether `conn` needs to be released before\n # returning/raising/recursing. Update this variable if necessary, and\n # leave `release_conn` constant throughout the function. That way, if\n # the function recurses, the original value of `release_conn` will be\n # passed down into the recursive call, and its value will be respected.\n #\n # See issue #651 [1] for details.\n #\n # [1] \n release_this_conn = release_conn\n \n http_tunnel_required = connection_requires_http_tunnel(\n self.proxy, self.proxy_config, destination_scheme\n )\n \n # Merge the proxy headers. Only done when not using HTTP CONNECT. We\n # have to copy the headers dict so we can safely change it without those\n # changes being reflected in anyone else's copy.\n if not http_tunnel_required:\n headers = headers.copy() # type: ignore[attr-defined]\n headers.update(self.proxy_headers) # type: ignore[union-attr]\n \n # Must keep the exception bound to a separate variable or else Python 3\n # complains about UnboundLocalError.\n err = None\n \n # Keep track of whether we cleanly exited the except block. This\n # ensures we do proper cleanup in finally.\n clean_exit = False\n \n # Rewind body position, if needed. Record current position\n # for future rewinds in the event of a redirect/retry.\n body_pos = set_file_position(body, body_pos)\n \n try:\n # Request a connection from the queue.\n timeout_obj = self._get_timeout(timeout)\n conn = self._get_conn(timeout=pool_timeout)\n \n conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment]\n \n # Is this a closed/new connection that requires CONNECT tunnelling?\n if self.proxy is not None and http_tunnel_required and conn.is_closed:\n try:\n self._prepare_proxy(conn)\n except (BaseSSLError, OSError, SocketTimeout) as e:\n self._raise_timeout(\n err=e, url=self.proxy.url, timeout_value=conn.timeout\n )\n raise\n \n # If we're going to release the connection in ``finally:``, then\n # the response doesn't need to know about the connection. Otherwise\n # it will also try to release it and we'll have a double-release\n # mess.\n response_conn = conn if not release_conn else None\n \n # Make the request on the HTTPConnection object\n> response = self._make_request(\n conn,\n method,\n url,\n timeout=timeout_obj,\n body=body,\n headers=headers,\n chunked=chunked,\n retries=retries,\n response_conn=response_conn,\n preload_content=preload_content,\n decode_content=decode_content,\n **response_kw,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request\n conn.request(\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request\n self.endheaders()\n.pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders\n self._send_output(message_body, encode_chunked=encode_chunked)\n.pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output\n self.send(msg)\n.pixi/envs/default/lib/python3.8/http/client.py:951: in send\n self.connect()\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect\n self.sock = self._new_conn()\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n except socket.gaierror as e:\n raise NameResolutionError(self.host, self, e) from e\n except SocketTimeout as e:\n raise ConnectTimeoutError(\n self,\n f\"Connection to {self.host} timed out. (connect timeout={self.timeout})\",\n ) from e\n \n except OSError as e:\n> raise NewConnectionError(\n self, f\"Failed to establish a new connection: {e}\"\n ) from e\nE urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n> resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen\n retries = retries.increment(\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nmethod = 'GET', url = '/demo/None', response = None\nerror = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')\n_pool = \n_stacktrace = \n\n def increment(\n self,\n method: str | None = None,\n url: str | None = None,\n response: BaseHTTPResponse | None = None,\n error: Exception | None = None,\n _pool: ConnectionPool | None = None,\n _stacktrace: TracebackType | None = None,\n ) -> Self:\n \"\"\"Return a new Retry object with incremented retry counters.\n \n :param response: A response object, or None, if the server did not\n return a response.\n :type response: :class:`~urllib3.response.BaseHTTPResponse`\n :param Exception error: An error encountered during the request, or\n None if the response was received successfully.\n \n :return: A new ``Retry`` object.\n \"\"\"\n if self.total is False and error:\n # Disabled, indicate to re-raise the error.\n raise reraise(type(error), error, _stacktrace)\n \n total = self.total\n if total is not None:\n total -= 1\n \n connect = self.connect\n read = self.read\n redirect = self.redirect\n status_count = self.status\n other = self.other\n cause = \"unknown\"\n status = None\n redirect_location = None\n \n if error and self._is_connection_error(error):\n # Connect retry?\n if connect is False:\n raise reraise(type(error), error, _stacktrace)\n elif connect is not None:\n connect -= 1\n \n elif error and self._is_read_error(error):\n # Read retry?\n if read is False or method is None or not self._is_method_retryable(method):\n raise reraise(type(error), error, _stacktrace)\n elif read is not None:\n read -= 1\n \n elif error:\n # Other retry?\n if other is not None:\n other -= 1\n \n elif response and response.get_redirect_location():\n # Redirect retry?\n if redirect is not None:\n redirect -= 1\n cause = \"too many redirects\"\n response_redirect_location = response.get_redirect_location()\n if response_redirect_location:\n redirect_location = response_redirect_location\n status = response.status\n \n else:\n # Incrementing because of a server error like a 500 in\n # status_forcelist and the given method is in the allowed_methods\n cause = ResponseError.GENERIC_ERROR\n if response and response.status:\n if status_count is not None:\n status_count -= 1\n cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status)\n status = response.status\n \n history = self.history + (\n RequestHistory(method, url, error, status, redirect_location),\n )\n \n new_retry = self.new(\n total=total,\n connect=connect,\n read=read,\n redirect=redirect,\n status=status_count,\n other=other,\n history=history,\n )\n \n if new_retry.is_exhausted():\n reason = error or ResponseError(cause)\n> raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]\nE urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError\n\nDuring handling of the above exception, another exception occurred:\n\nself = , msg_id = None\ntimeout = None\n\n def _check_if_message_on_server(self, msg_id, timeout=None):\n \"\"\"Try to load page for specific message. If there is a html tag like then there is no\n such message.\n \n :param msg_id: ID of message to be checked\n :params timeout: The value of timeout to be passed to the get request\n :return:\n \"\"\"\n \n request_headers = dict()\n if self._user or self._password:\n request_headers['Cookie'] = self._make_user_and_pswd_cookie()\n try:\n> response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False,\n verify=False, timeout=timeout)\n\nslic/utils/logbook.py:570: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:73: in get\n return request(\"get\", url, params=params, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request\n return session.request(method=method, url=url, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request\n resp = self.send(prep, **send_kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send\n r = adapter.send(request, **kwargs)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n \n except (ProtocolError, OSError) as err:\n raise ConnectionError(err, request=request)\n \n except MaxRetryError as e:\n if isinstance(e.reason, ConnectTimeoutError):\n # TODO: Remove this in 3.0.0: see #2811\n if not isinstance(e.reason, NewConnectionError):\n raise ConnectTimeout(e, request=request)\n \n if isinstance(e.reason, ResponseError):\n raise RetryError(e, request=request)\n \n if isinstance(e.reason, _ProxyError):\n raise ProxyError(e, request=request)\n \n if isinstance(e.reason, _SSLError):\n # This branch is for urllib3 v1.22 and later.\n raise SSLError(e, request=request)\n \n> raise ConnectionError(e, request=request)\nE requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError\n\nDuring handling of the above exception, another exception occurred:\n\n def test_get_default_elog_instance_with_direct_password_and_real_check():\n url = \"http://localhost:8080/demo\"\n user = \"robot\"\n password = \"testpassword\"\n text = \"This is a message1\"\n \n elog = Elog(\"http://localhost:8080/demo\", user=user, password=password, encrypt_pwd=False)\n \n try:\n> elog.post(text)\n\ntests/test_utils_elog.py:22: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \nslic/utils/elog.py:17: in post\n return self._log.post(*args, **kwargs)\nslic/utils/logbook.py:289: in post\n self._check_if_message_on_server(msg_id) # Raises exceptions if no message or no response from server\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = , msg_id = None\ntimeout = None\n\n def _check_if_message_on_server(self, msg_id, timeout=None):\n \"\"\"Try to load page for specific message. If there is a html tag like then there is no\n such message.\n \n :param msg_id: ID of message to be checked\n :params timeout: The value of timeout to be passed to the get request\n :return:\n \"\"\"\n \n request_headers = dict()\n if self._user or self._password:\n request_headers['Cookie'] = self._make_user_and_pswd_cookie()\n try:\n response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False,\n verify=False, timeout=timeout)\n \n # If there is no message code 200 will be returned (OK) and _validate_response will not recognise it\n # but there will be some error in the html code.\n resp_message, resp_headers, resp_msg_id = _validate_response(response)\n # If there is no message, code 200 will be returned (OK) but there will be some error indication in\n # the html code.\n if re.findall('.*?',\n resp_message.decode('utf-8', 'ignore'),\n flags=re.DOTALL):\n raise LogbookInvalidMessageID('Message with ID: ' + str(msg_id) + ' does not exist on logbook.')\n \n except requests.Timeout as e:\n # Catch here a timeout o the post request.\n # Raise the logbook exception and let the user handle it\n raise LogbookServerTimeout('{0} method cannot be completed because of a network timeout:\\n' +\n '{1}'.format(sys._getframe().f_code.co_name, e))\n \n except requests.RequestException as e:\n> raise LogbookServerProblem('No response from the logbook server.\\nDetails: ' + '{0}'.format(e))\nE slic.utils.logbook_exceptions.LogbookServerProblem: No response from the logbook server.\nE Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\nslic/utils/logbook.py:590: LogbookServerProblem\n\nDuring handling of the above exception, another exception occurred:\n\n def test_get_default_elog_instance_with_direct_password_and_real_check():\n url = \"http://localhost:8080/demo\"\n user = \"robot\"\n password = \"testpassword\"\n text = \"This is a message1\"\n \n elog = Elog(\"http://localhost:8080/demo\", user=user, password=password, encrypt_pwd=False)\n \n try:\n elog.post(text)\n except Exception as e:\n> pytest.fail(f\"elog.open() raised an unexpected exception: {e}\")\nE Failed: elog.open() raised an unexpected exception: No response from the logbook server.\nE Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\ntests/test_utils_elog.py:24: Failed"}, "teardown": {"duration": 0.00584752019494772, "outcome": "passed"}}, {"nodeid": "tests/test_utils_elog.py::test_get_default_elog_instance_with_wrong_password_and_real_check", "lineno": 37, "outcome": "failed", "keywords": ["test_get_default_elog_instance_with_wrong_password_and_real_check", "test_utils_elog.py", "tests", "slic", ""], "setup": {"duration": 0.00016171811148524284, "outcome": "passed"}, "call": {"duration": 0.0003640740178525448, "outcome": "failed", "crash": {"path": "/workspace/tligui_y/slic/tests/test_utils_elog.py", "lineno": 44, "message": "Failed: DID NOT RAISE "}, "traceback": [{"path": "tests/test_utils_elog.py", "lineno": 44, "message": "Failed"}], "longrepr": "def test_get_default_elog_instance_with_wrong_password_and_real_check():\n url = \"http://localhost:8080/demo\"\n user = \"robot\"\n wrong_password = \"wrongpassword\"\n \n with pytest.raises(LogbookAuthenticationError):\n> Elog(url, user=user, password=wrong_password, encrypt_pwd=False)\nE Failed: DID NOT RAISE \n\ntests/test_utils_elog.py:44: Failed"}, "teardown": {"duration": 0.0002082372084259987, "outcome": "passed"}}, {"nodeid": "tests/test_utils_elog.py::test_get_default_elog_instance_asks_password_and_opens", "lineno": 46, "outcome": "failed", "keywords": ["test_get_default_elog_instance_asks_password_and_opens", "__wrapped__", "patchings", "test_utils_elog.py", "tests", "slic", ""], "setup": {"duration": 0.00017797807231545448, "outcome": "passed"}, "call": {"duration": 0.005512538366019726, "outcome": "failed", "crash": {"path": "/workspace/tligui_y/slic/tests/test_utils_elog.py", "lineno": 61, "message": "Failed: elog.open() raised an unexpected exception: No response from the logbook server.\nDetails: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))"}, "traceback": [{"path": "tests/test_utils_elog.py", "lineno": 61, "message": "Failed"}], "longrepr": "self = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n> sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection\n raise err\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\naddress = ('localhost', 8080), timeout = None, source_address = None\nsocket_options = [(6, 1, 1)]\n\n def create_connection(\n address: tuple[str, int],\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n source_address: tuple[str, int] | None = None,\n socket_options: _TYPE_SOCKET_OPTIONS | None = None,\n ) -> socket.socket:\n \"\"\"Connect to *address* and return the socket object.\n \n Convenience function. Connect to *address* (a 2-tuple ``(host,\n port)``) and return the socket object. Passing the optional\n *timeout* parameter will set the timeout on the socket instance\n before attempting to connect. If no *timeout* is supplied, the\n global default timeout setting returned by :func:`socket.getdefaulttimeout`\n is used. If *source_address* is set it must be a tuple of (host, port)\n for the socket to bind as a source address before making the connection.\n An host of '' or port 0 tells the OS to use the default.\n \"\"\"\n \n host, port = address\n if host.startswith(\"[\"):\n host = host.strip(\"[]\")\n err = None\n \n # Using the value from allowed_gai_family() in the context of getaddrinfo lets\n # us select whether to work with IPv4 DNS records, IPv6 records, or both.\n # The original create_connection function always returns all records.\n family = allowed_gai_family()\n \n try:\n host.encode(\"idna\")\n except UnicodeError:\n raise LocationParseError(f\"'{host}', label empty or too long\") from None\n \n for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):\n af, socktype, proto, canonname, sa = res\n sock = None\n try:\n sock = socket.socket(af, socktype, proto)\n \n # If provided, set socket level options before connecting.\n _set_socket_options(sock, socket_options)\n \n if timeout is not _DEFAULT_TIMEOUT:\n sock.settimeout(timeout)\n if source_address:\n sock.bind(source_address)\n> sock.connect(sa)\nE ConnectionRefusedError: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nmethod = 'POST', url = '/demo/'\nbody = b'--f5f039deda62b157d5b094f465ce4716\\r\\nContent-Disposition: form-data; name=\"Author\"\\r\\n\\r\\nrobot\\r\\n--f5f039deda62b1...Disposition: form-data; name=\"Text\"; filename=\"\"\\r\\n\\r\\nThis is a message2\\r\\n--f5f039deda62b157d5b094f465ce4716--\\r\\n'\nheaders = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '705', 'Content-Type': 'multipart/form-data; boundary=f5f039deda62b157d5b094f465ce4716'}\nretries = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nredirect = False, assert_same_host = False\ntimeout = Timeout(connect=None, read=None, total=None), pool_timeout = None\nrelease_conn = False, chunked = False, body_pos = None, preload_content = False\ndecode_content = False, response_kw = {}\nparsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/', query=None, fragment=None)\ndestination_scheme = None, conn = None, release_this_conn = True\nhttp_tunnel_required = False, err = None, clean_exit = False\n\n def urlopen( # type: ignore[override]\n self,\n method: str,\n url: str,\n body: _TYPE_BODY | None = None,\n headers: typing.Mapping[str, str] | None = None,\n retries: Retry | bool | int | None = None,\n redirect: bool = True,\n assert_same_host: bool = True,\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n pool_timeout: int | None = None,\n release_conn: bool | None = None,\n chunked: bool = False,\n body_pos: _TYPE_BODY_POSITION | None = None,\n preload_content: bool = True,\n decode_content: bool = True,\n **response_kw: typing.Any,\n ) -> BaseHTTPResponse:\n \"\"\"\n Get a connection from the pool and perform an HTTP request. This is the\n lowest level call for making a request, so you'll need to specify all\n the raw details.\n \n .. note::\n \n More commonly, it's appropriate to use a convenience method\n such as :meth:`request`.\n \n .. note::\n \n `release_conn` will only behave as expected if\n `preload_content=False` because we want to make\n `preload_content=False` the default behaviour someday soon without\n breaking backwards compatibility.\n \n :param method:\n HTTP request method (such as GET, POST, PUT, etc.)\n \n :param url:\n The URL to perform the request on.\n \n :param body:\n Data to send in the request body, either :class:`str`, :class:`bytes`,\n an iterable of :class:`str`/:class:`bytes`, or a file-like object.\n \n :param headers:\n Dictionary of custom headers to send, such as User-Agent,\n If-None-Match, etc. If None, pool headers are used. If provided,\n these headers completely replace any pool-specific headers.\n \n :param retries:\n Configure the number of retries to allow before raising a\n :class:`~urllib3.exceptions.MaxRetryError` exception.\n \n If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a\n :class:`~urllib3.util.retry.Retry` object for fine-grained control\n over different types of retries.\n Pass an integer number to retry connection errors that many times,\n but no other types of errors. Pass zero to never retry.\n \n If ``False``, then retries are disabled and any exception is raised\n immediately. Also, instead of raising a MaxRetryError on redirects,\n the redirect response will be returned.\n \n :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int.\n \n :param redirect:\n If True, automatically handle redirects (status codes 301, 302,\n 303, 307, 308). Each redirect counts as a retry. Disabling retries\n will disable redirect, too.\n \n :param assert_same_host:\n If ``True``, will make sure that the host of the pool requests is\n consistent else will raise HostChangedError. When ``False``, you can\n use the pool on an HTTP proxy and request foreign hosts.\n \n :param timeout:\n If specified, overrides the default timeout for this one\n request. It may be a float (in seconds) or an instance of\n :class:`urllib3.util.Timeout`.\n \n :param pool_timeout:\n If set and the pool is set to block=True, then this method will\n block for ``pool_timeout`` seconds and raise EmptyPoolError if no\n connection is available within the time period.\n \n :param bool preload_content:\n If True, the response's body will be preloaded into memory.\n \n :param bool decode_content:\n If True, will attempt to decode the body based on the\n 'content-encoding' header.\n \n :param release_conn:\n If False, then the urlopen call will not release the connection\n back into the pool once a response is received (but will release if\n you read the entire contents of the response such as when\n `preload_content=True`). This is useful if you're not preloading\n the response's content immediately. You will need to call\n ``r.release_conn()`` on the response ``r`` to return the connection\n back into the pool. If None, it takes the value of ``preload_content``\n which defaults to ``True``.\n \n :param bool chunked:\n If True, urllib3 will send the body using chunked transfer\n encoding. Otherwise, urllib3 will send the body using the standard\n content-length form. Defaults to False.\n \n :param int body_pos:\n Position to seek to in file-like body in the event of a retry or\n redirect. Typically this won't need to be set because urllib3 will\n auto-populate the value when needed.\n \"\"\"\n parsed_url = parse_url(url)\n destination_scheme = parsed_url.scheme\n \n if headers is None:\n headers = self.headers\n \n if not isinstance(retries, Retry):\n retries = Retry.from_int(retries, redirect=redirect, default=self.retries)\n \n if release_conn is None:\n release_conn = preload_content\n \n # Check host\n if assert_same_host and not self.is_same_host(url):\n raise HostChangedError(self, url, retries)\n \n # Ensure that the URL we're connecting to is properly encoded\n if url.startswith(\"/\"):\n url = to_str(_encode_target(url))\n else:\n url = to_str(parsed_url.url)\n \n conn = None\n \n # Track whether `conn` needs to be released before\n # returning/raising/recursing. Update this variable if necessary, and\n # leave `release_conn` constant throughout the function. That way, if\n # the function recurses, the original value of `release_conn` will be\n # passed down into the recursive call, and its value will be respected.\n #\n # See issue #651 [1] for details.\n #\n # [1] \n release_this_conn = release_conn\n \n http_tunnel_required = connection_requires_http_tunnel(\n self.proxy, self.proxy_config, destination_scheme\n )\n \n # Merge the proxy headers. Only done when not using HTTP CONNECT. We\n # have to copy the headers dict so we can safely change it without those\n # changes being reflected in anyone else's copy.\n if not http_tunnel_required:\n headers = headers.copy() # type: ignore[attr-defined]\n headers.update(self.proxy_headers) # type: ignore[union-attr]\n \n # Must keep the exception bound to a separate variable or else Python 3\n # complains about UnboundLocalError.\n err = None\n \n # Keep track of whether we cleanly exited the except block. This\n # ensures we do proper cleanup in finally.\n clean_exit = False\n \n # Rewind body position, if needed. Record current position\n # for future rewinds in the event of a redirect/retry.\n body_pos = set_file_position(body, body_pos)\n \n try:\n # Request a connection from the queue.\n timeout_obj = self._get_timeout(timeout)\n conn = self._get_conn(timeout=pool_timeout)\n \n conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment]\n \n # Is this a closed/new connection that requires CONNECT tunnelling?\n if self.proxy is not None and http_tunnel_required and conn.is_closed:\n try:\n self._prepare_proxy(conn)\n except (BaseSSLError, OSError, SocketTimeout) as e:\n self._raise_timeout(\n err=e, url=self.proxy.url, timeout_value=conn.timeout\n )\n raise\n \n # If we're going to release the connection in ``finally:``, then\n # the response doesn't need to know about the connection. Otherwise\n # it will also try to release it and we'll have a double-release\n # mess.\n response_conn = conn if not release_conn else None\n \n # Make the request on the HTTPConnection object\n> response = self._make_request(\n conn,\n method,\n url,\n timeout=timeout_obj,\n body=body,\n headers=headers,\n chunked=chunked,\n retries=retries,\n response_conn=response_conn,\n preload_content=preload_content,\n decode_content=decode_content,\n **response_kw,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request\n conn.request(\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request\n self.endheaders()\n.pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders\n self._send_output(message_body, encode_chunked=encode_chunked)\n.pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output\n self.send(msg)\n.pixi/envs/default/lib/python3.8/http/client.py:951: in send\n self.connect()\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect\n self.sock = self._new_conn()\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n except socket.gaierror as e:\n raise NameResolutionError(self.host, self, e) from e\n except SocketTimeout as e:\n raise ConnectTimeoutError(\n self,\n f\"Connection to {self.host} timed out. (connect timeout={self.timeout})\",\n ) from e\n \n except OSError as e:\n> raise NewConnectionError(\n self, f\"Failed to establish a new connection: {e}\"\n ) from e\nE urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n> resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen\n retries = retries.increment(\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nmethod = 'POST', url = '/demo/', response = None\nerror = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')\n_pool = \n_stacktrace = \n\n def increment(\n self,\n method: str | None = None,\n url: str | None = None,\n response: BaseHTTPResponse | None = None,\n error: Exception | None = None,\n _pool: ConnectionPool | None = None,\n _stacktrace: TracebackType | None = None,\n ) -> Self:\n \"\"\"Return a new Retry object with incremented retry counters.\n \n :param response: A response object, or None, if the server did not\n return a response.\n :type response: :class:`~urllib3.response.BaseHTTPResponse`\n :param Exception error: An error encountered during the request, or\n None if the response was received successfully.\n \n :return: A new ``Retry`` object.\n \"\"\"\n if self.total is False and error:\n # Disabled, indicate to re-raise the error.\n raise reraise(type(error), error, _stacktrace)\n \n total = self.total\n if total is not None:\n total -= 1\n \n connect = self.connect\n read = self.read\n redirect = self.redirect\n status_count = self.status\n other = self.other\n cause = \"unknown\"\n status = None\n redirect_location = None\n \n if error and self._is_connection_error(error):\n # Connect retry?\n if connect is False:\n raise reraise(type(error), error, _stacktrace)\n elif connect is not None:\n connect -= 1\n \n elif error and self._is_read_error(error):\n # Read retry?\n if read is False or method is None or not self._is_method_retryable(method):\n raise reraise(type(error), error, _stacktrace)\n elif read is not None:\n read -= 1\n \n elif error:\n # Other retry?\n if other is not None:\n other -= 1\n \n elif response and response.get_redirect_location():\n # Redirect retry?\n if redirect is not None:\n redirect -= 1\n cause = \"too many redirects\"\n response_redirect_location = response.get_redirect_location()\n if response_redirect_location:\n redirect_location = response_redirect_location\n status = response.status\n \n else:\n # Incrementing because of a server error like a 500 in\n # status_forcelist and the given method is in the allowed_methods\n cause = ResponseError.GENERIC_ERROR\n if response and response.status:\n if status_count is not None:\n status_count -= 1\n cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status)\n status = response.status\n \n history = self.history + (\n RequestHistory(method, url, error, status, redirect_location),\n )\n \n new_retry = self.new(\n total=total,\n connect=connect,\n read=read,\n redirect=redirect,\n status=status_count,\n other=other,\n history=history,\n )\n \n if new_retry.is_exhausted():\n reason = error or ResponseError(cause)\n> raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]\nE urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError\n\nDuring handling of the above exception, another exception occurred:\n\nself = \nmessage = 'This is a message2', msg_id = None, reply = False\nattributes = {'Author': 'robot', 'When': 1755182070, 'cmd': 'Submit', 'exp': 'demo', ...}\nattachments = [], suppress_email_notification = False, encoding = None\ntimeout = None, kwargs = {'Author': 'robot'}\nlogbook_directory = 'elog_instance/logbooks/demo'\nnew_attachment_list = [('Text', ('', b'This is a message2'))]\nobjects_to_close = []\nattributes_to_edit = {'Author': b'robot', 'When': 1755182070, 'cmd': b'Submit', 'exp': b'demo', ...}\n\n def post(self, message, msg_id=None, reply=False, attributes=None, attachments=None,\n suppress_email_notification=False, encoding=None, timeout=None, **kwargs):\n \"\"\"\n Posts message to the logbook. If msg_id is not specified new message will be created, otherwise existing\n message will be edited, or a reply (if reply=True) to it will be created. This method returns the msg_id\n of the newly created message.\n \"\"\"\n \n logbook_directory = \"elog_instance/logbooks/demo\"\n print(f\"Checking the existence of the directory {logbook_directory}\")\n \n # Check if the directory exists\n if not os.path.exists(logbook_directory):\n print(f\"The directory {logbook_directory} does not exist.\")\n else:\n print(f\"The directory {logbook_directory} exists.\")\n \n # Check write permissions\n if os.access(logbook_directory, os.W_OK):\n print(f\"The directory {logbook_directory} has write permissions.\")\n else:\n print(f\"The directory {logbook_directory} does not have write permissions.\")\n \n \n print(\"STARTING POST\")\n # Ajout des impressions pour d\u00e9boguer\n print(f\"Message to post: {message}\")\n print(f\"msg_id: {msg_id}\")\n print(f\"Attributes: {attributes}\")\n print(f\"Attachments: {attachments}\")\n print(f\"Encoding: {encoding}\")\n print(f\"Timeout: {timeout}\")\n print(f\"Additional kwargs: {kwargs}\")\n \n attributes = attributes or {}\n attributes = {**attributes, **kwargs} # kwargs as attributes with higher priority\n print(f\"Updated attributes: {attributes}\")\n \n attachments = attachments or []\n print(f\"Attachments list: {attachments}\")\n \n if encoding is not None:\n if encoding not in ['plain', 'HTML', 'ELCode']:\n raise LogbookMessageRejected('Invalid message encoding. Valid options: plain, HTML, ELCode.')\n attributes['Encoding'] = encoding\n \n if suppress_email_notification:\n attributes[\"suppress\"] = 1\n \n # Prepare attachments\n if attachments:\n new_attachment_list, objects_to_close = self._prepare_attachments(attachments)\n print(f\"New attachments prepared: {new_attachment_list}\")\n else:\n objects_to_close = []\n new_attachment_list = []\n \n attributes_to_edit = dict()\n \n if msg_id:\n print(f\"Editing message with msg_id: {msg_id}\")\n if reply:\n print(f\"Replying to message with msg_id: {msg_id}\")\n self._check_if_message_on_server(msg_id)\n attributes['reply_to'] = str(msg_id)\n else:\n print(\"Editing existing message.\")\n attributes['edit_id'] = str(msg_id)\n attributes['skiplock'] = '1'\n msg_to_edit, attributes_to_edit, existing_attachments_list = self.read(msg_id)\n \n # Merge new attributes\n for attribute, data in attributes.items():\n if data is not None:\n attributes_to_edit[attribute] = data\n \n print(f\"Attributes after merging: {attributes_to_edit}\")\n \n # Process existing attachments\n i = 0\n existing_attachments_filename_list = []\n for attachment in existing_attachments_list:\n attributes_to_edit[f'attachment{i}'] = os.path.basename(attachment)\n existing_attachments_filename_list.append(os.path.basename(attachment)[14:])\n i += 1\n \n print(f\"Existing attachments: {existing_attachments_filename_list}\")\n \n duplicate_attachment_list = []\n for new_attachment in new_attachment_list:\n new_attachment_filename = new_attachment[1][0]\n print(f\"Checking new attachment: {new_attachment_filename}\")\n if new_attachment_filename in existing_attachments_filename_list:\n # Same attachment exists on the server, compare content\n new_attachment_content = new_attachment[1][1].read()\n new_attachment[1][1].seek(0)\n attachment_index = existing_attachments_filename_list.index(new_attachment_filename)\n existing_attachment_content = self.download_attachment(\n url=existing_attachments_list[attachment_index],\n timeout=timeout\n )\n if new_attachment_content == existing_attachment_content:\n print(f\"Duplicate attachment detected: {new_attachment_filename}\")\n duplicate_attachment_list.append(new_attachment)\n else:\n print(f\"Attachment content has changed: {new_attachment_filename}\")\n self.delete_attachment(msg_id, attributes=attributes_to_edit,\n attachment_id=attachment_index,\n timeout=timeout, text=msg_to_edit)\n existing_attachments_filename_list.pop(attachment_index)\n existing_attachments_list.pop(attachment_index)\n \n print(f\"Duplicate attachments to remove: {duplicate_attachment_list}\")\n \n # Remove duplicates\n for attach in duplicate_attachment_list:\n new_attachment_list.remove(attach)\n \n print(f\"Final new attachments list: {new_attachment_list}\")\n else:\n # Creating a new message, add timestamp if not present\n if 'When' not in attributes:\n attributes['When'] = int(datetime.now().timestamp())\n \n # Final check on attributes\n if not attributes_to_edit:\n attributes_to_edit = attributes\n \n print(f\"Final attributes to send to the server: {attributes_to_edit}\")\n \n # Remove reserved attributes\n _remove_reserved_attributes(attributes_to_edit)\n \n new_attachment_list.append(('Text', ('', message.encode('iso-8859-1'))))\n print(f\"Final attachment list including message text: {new_attachment_list}\")\n \n # Add base message attributes\n self._add_base_msg_attributes(attributes_to_edit)\n print(f\"Attributes with base message added: {attributes_to_edit}\")\n \n # Sanitize attribute keys\n attributes_to_edit = _replace_special_characters_in_attribute_keys(attributes_to_edit)\n print(f\"Attributes after sanitizing keys: {attributes_to_edit}\")\n \n # Encode all string values in latin1\n attributes_to_edit = _encode_values(attributes_to_edit)\n print(f\"Attributes after encoding: {attributes_to_edit}\")\n \n try:\n print(\"Sending POST request to the server...\")\n> response = requests.post(self._url, data=attributes_to_edit, files=new_attachment_list,\n allow_redirects=False, verify=False, timeout=timeout)\n\nslic/utils/logbook.py:269: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:115: in post\n return request(\"post\", url, data=data, json=json, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request\n return session.request(method=method, url=url, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request\n resp = self.send(prep, **send_kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send\n r = adapter.send(request, **kwargs)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n \n except (ProtocolError, OSError) as err:\n raise ConnectionError(err, request=request)\n \n except MaxRetryError as e:\n if isinstance(e.reason, ConnectTimeoutError):\n # TODO: Remove this in 3.0.0: see #2811\n if not isinstance(e.reason, NewConnectionError):\n raise ConnectTimeout(e, request=request)\n \n if isinstance(e.reason, ResponseError):\n raise RetryError(e, request=request)\n \n if isinstance(e.reason, _ProxyError):\n raise ProxyError(e, request=request)\n \n if isinstance(e.reason, _SSLError):\n # This branch is for urllib3 v1.22 and later.\n raise SSLError(e, request=request)\n \n> raise ConnectionError(e, request=request)\nE requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError\n\nDuring handling of the above exception, another exception occurred:\n\nself = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n> sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection\n raise err\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\naddress = ('localhost', 8080), timeout = None, source_address = None\nsocket_options = [(6, 1, 1)]\n\n def create_connection(\n address: tuple[str, int],\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n source_address: tuple[str, int] | None = None,\n socket_options: _TYPE_SOCKET_OPTIONS | None = None,\n ) -> socket.socket:\n \"\"\"Connect to *address* and return the socket object.\n \n Convenience function. Connect to *address* (a 2-tuple ``(host,\n port)``) and return the socket object. Passing the optional\n *timeout* parameter will set the timeout on the socket instance\n before attempting to connect. If no *timeout* is supplied, the\n global default timeout setting returned by :func:`socket.getdefaulttimeout`\n is used. If *source_address* is set it must be a tuple of (host, port)\n for the socket to bind as a source address before making the connection.\n An host of '' or port 0 tells the OS to use the default.\n \"\"\"\n \n host, port = address\n if host.startswith(\"[\"):\n host = host.strip(\"[]\")\n err = None\n \n # Using the value from allowed_gai_family() in the context of getaddrinfo lets\n # us select whether to work with IPv4 DNS records, IPv6 records, or both.\n # The original create_connection function always returns all records.\n family = allowed_gai_family()\n \n try:\n host.encode(\"idna\")\n except UnicodeError:\n raise LocationParseError(f\"'{host}', label empty or too long\") from None\n \n for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):\n af, socktype, proto, canonname, sa = res\n sock = None\n try:\n sock = socket.socket(af, socktype, proto)\n \n # If provided, set socket level options before connecting.\n _set_socket_options(sock, socket_options)\n \n if timeout is not _DEFAULT_TIMEOUT:\n sock.settimeout(timeout)\n if source_address:\n sock.bind(source_address)\n> sock.connect(sa)\nE ConnectionRefusedError: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nmethod = 'GET', url = '/demo/None', body = None\nheaders = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'unm=robot;upwd=testpassword;'}\nretries = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nredirect = False, assert_same_host = False\ntimeout = Timeout(connect=None, read=None, total=None), pool_timeout = None\nrelease_conn = False, chunked = False, body_pos = None, preload_content = False\ndecode_content = False, response_kw = {}\nparsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/None', query=None, fragment=None)\ndestination_scheme = None, conn = None, release_this_conn = True\nhttp_tunnel_required = False, err = None, clean_exit = False\n\n def urlopen( # type: ignore[override]\n self,\n method: str,\n url: str,\n body: _TYPE_BODY | None = None,\n headers: typing.Mapping[str, str] | None = None,\n retries: Retry | bool | int | None = None,\n redirect: bool = True,\n assert_same_host: bool = True,\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n pool_timeout: int | None = None,\n release_conn: bool | None = None,\n chunked: bool = False,\n body_pos: _TYPE_BODY_POSITION | None = None,\n preload_content: bool = True,\n decode_content: bool = True,\n **response_kw: typing.Any,\n ) -> BaseHTTPResponse:\n \"\"\"\n Get a connection from the pool and perform an HTTP request. This is the\n lowest level call for making a request, so you'll need to specify all\n the raw details.\n \n .. note::\n \n More commonly, it's appropriate to use a convenience method\n such as :meth:`request`.\n \n .. note::\n \n `release_conn` will only behave as expected if\n `preload_content=False` because we want to make\n `preload_content=False` the default behaviour someday soon without\n breaking backwards compatibility.\n \n :param method:\n HTTP request method (such as GET, POST, PUT, etc.)\n \n :param url:\n The URL to perform the request on.\n \n :param body:\n Data to send in the request body, either :class:`str`, :class:`bytes`,\n an iterable of :class:`str`/:class:`bytes`, or a file-like object.\n \n :param headers:\n Dictionary of custom headers to send, such as User-Agent,\n If-None-Match, etc. If None, pool headers are used. If provided,\n these headers completely replace any pool-specific headers.\n \n :param retries:\n Configure the number of retries to allow before raising a\n :class:`~urllib3.exceptions.MaxRetryError` exception.\n \n If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a\n :class:`~urllib3.util.retry.Retry` object for fine-grained control\n over different types of retries.\n Pass an integer number to retry connection errors that many times,\n but no other types of errors. Pass zero to never retry.\n \n If ``False``, then retries are disabled and any exception is raised\n immediately. Also, instead of raising a MaxRetryError on redirects,\n the redirect response will be returned.\n \n :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int.\n \n :param redirect:\n If True, automatically handle redirects (status codes 301, 302,\n 303, 307, 308). Each redirect counts as a retry. Disabling retries\n will disable redirect, too.\n \n :param assert_same_host:\n If ``True``, will make sure that the host of the pool requests is\n consistent else will raise HostChangedError. When ``False``, you can\n use the pool on an HTTP proxy and request foreign hosts.\n \n :param timeout:\n If specified, overrides the default timeout for this one\n request. It may be a float (in seconds) or an instance of\n :class:`urllib3.util.Timeout`.\n \n :param pool_timeout:\n If set and the pool is set to block=True, then this method will\n block for ``pool_timeout`` seconds and raise EmptyPoolError if no\n connection is available within the time period.\n \n :param bool preload_content:\n If True, the response's body will be preloaded into memory.\n \n :param bool decode_content:\n If True, will attempt to decode the body based on the\n 'content-encoding' header.\n \n :param release_conn:\n If False, then the urlopen call will not release the connection\n back into the pool once a response is received (but will release if\n you read the entire contents of the response such as when\n `preload_content=True`). This is useful if you're not preloading\n the response's content immediately. You will need to call\n ``r.release_conn()`` on the response ``r`` to return the connection\n back into the pool. If None, it takes the value of ``preload_content``\n which defaults to ``True``.\n \n :param bool chunked:\n If True, urllib3 will send the body using chunked transfer\n encoding. Otherwise, urllib3 will send the body using the standard\n content-length form. Defaults to False.\n \n :param int body_pos:\n Position to seek to in file-like body in the event of a retry or\n redirect. Typically this won't need to be set because urllib3 will\n auto-populate the value when needed.\n \"\"\"\n parsed_url = parse_url(url)\n destination_scheme = parsed_url.scheme\n \n if headers is None:\n headers = self.headers\n \n if not isinstance(retries, Retry):\n retries = Retry.from_int(retries, redirect=redirect, default=self.retries)\n \n if release_conn is None:\n release_conn = preload_content\n \n # Check host\n if assert_same_host and not self.is_same_host(url):\n raise HostChangedError(self, url, retries)\n \n # Ensure that the URL we're connecting to is properly encoded\n if url.startswith(\"/\"):\n url = to_str(_encode_target(url))\n else:\n url = to_str(parsed_url.url)\n \n conn = None\n \n # Track whether `conn` needs to be released before\n # returning/raising/recursing. Update this variable if necessary, and\n # leave `release_conn` constant throughout the function. That way, if\n # the function recurses, the original value of `release_conn` will be\n # passed down into the recursive call, and its value will be respected.\n #\n # See issue #651 [1] for details.\n #\n # [1] \n release_this_conn = release_conn\n \n http_tunnel_required = connection_requires_http_tunnel(\n self.proxy, self.proxy_config, destination_scheme\n )\n \n # Merge the proxy headers. Only done when not using HTTP CONNECT. We\n # have to copy the headers dict so we can safely change it without those\n # changes being reflected in anyone else's copy.\n if not http_tunnel_required:\n headers = headers.copy() # type: ignore[attr-defined]\n headers.update(self.proxy_headers) # type: ignore[union-attr]\n \n # Must keep the exception bound to a separate variable or else Python 3\n # complains about UnboundLocalError.\n err = None\n \n # Keep track of whether we cleanly exited the except block. This\n # ensures we do proper cleanup in finally.\n clean_exit = False\n \n # Rewind body position, if needed. Record current position\n # for future rewinds in the event of a redirect/retry.\n body_pos = set_file_position(body, body_pos)\n \n try:\n # Request a connection from the queue.\n timeout_obj = self._get_timeout(timeout)\n conn = self._get_conn(timeout=pool_timeout)\n \n conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment]\n \n # Is this a closed/new connection that requires CONNECT tunnelling?\n if self.proxy is not None and http_tunnel_required and conn.is_closed:\n try:\n self._prepare_proxy(conn)\n except (BaseSSLError, OSError, SocketTimeout) as e:\n self._raise_timeout(\n err=e, url=self.proxy.url, timeout_value=conn.timeout\n )\n raise\n \n # If we're going to release the connection in ``finally:``, then\n # the response doesn't need to know about the connection. Otherwise\n # it will also try to release it and we'll have a double-release\n # mess.\n response_conn = conn if not release_conn else None\n \n # Make the request on the HTTPConnection object\n> response = self._make_request(\n conn,\n method,\n url,\n timeout=timeout_obj,\n body=body,\n headers=headers,\n chunked=chunked,\n retries=retries,\n response_conn=response_conn,\n preload_content=preload_content,\n decode_content=decode_content,\n **response_kw,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request\n conn.request(\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request\n self.endheaders()\n.pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders\n self._send_output(message_body, encode_chunked=encode_chunked)\n.pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output\n self.send(msg)\n.pixi/envs/default/lib/python3.8/http/client.py:951: in send\n self.connect()\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect\n self.sock = self._new_conn()\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n except socket.gaierror as e:\n raise NameResolutionError(self.host, self, e) from e\n except SocketTimeout as e:\n raise ConnectTimeoutError(\n self,\n f\"Connection to {self.host} timed out. (connect timeout={self.timeout})\",\n ) from e\n \n except OSError as e:\n> raise NewConnectionError(\n self, f\"Failed to establish a new connection: {e}\"\n ) from e\nE urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n> resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen\n retries = retries.increment(\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nmethod = 'GET', url = '/demo/None', response = None\nerror = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')\n_pool = \n_stacktrace = \n\n def increment(\n self,\n method: str | None = None,\n url: str | None = None,\n response: BaseHTTPResponse | None = None,\n error: Exception | None = None,\n _pool: ConnectionPool | None = None,\n _stacktrace: TracebackType | None = None,\n ) -> Self:\n \"\"\"Return a new Retry object with incremented retry counters.\n \n :param response: A response object, or None, if the server did not\n return a response.\n :type response: :class:`~urllib3.response.BaseHTTPResponse`\n :param Exception error: An error encountered during the request, or\n None if the response was received successfully.\n \n :return: A new ``Retry`` object.\n \"\"\"\n if self.total is False and error:\n # Disabled, indicate to re-raise the error.\n raise reraise(type(error), error, _stacktrace)\n \n total = self.total\n if total is not None:\n total -= 1\n \n connect = self.connect\n read = self.read\n redirect = self.redirect\n status_count = self.status\n other = self.other\n cause = \"unknown\"\n status = None\n redirect_location = None\n \n if error and self._is_connection_error(error):\n # Connect retry?\n if connect is False:\n raise reraise(type(error), error, _stacktrace)\n elif connect is not None:\n connect -= 1\n \n elif error and self._is_read_error(error):\n # Read retry?\n if read is False or method is None or not self._is_method_retryable(method):\n raise reraise(type(error), error, _stacktrace)\n elif read is not None:\n read -= 1\n \n elif error:\n # Other retry?\n if other is not None:\n other -= 1\n \n elif response and response.get_redirect_location():\n # Redirect retry?\n if redirect is not None:\n redirect -= 1\n cause = \"too many redirects\"\n response_redirect_location = response.get_redirect_location()\n if response_redirect_location:\n redirect_location = response_redirect_location\n status = response.status\n \n else:\n # Incrementing because of a server error like a 500 in\n # status_forcelist and the given method is in the allowed_methods\n cause = ResponseError.GENERIC_ERROR\n if response and response.status:\n if status_count is not None:\n status_count -= 1\n cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status)\n status = response.status\n \n history = self.history + (\n RequestHistory(method, url, error, status, redirect_location),\n )\n \n new_retry = self.new(\n total=total,\n connect=connect,\n read=read,\n redirect=redirect,\n status=status_count,\n other=other,\n history=history,\n )\n \n if new_retry.is_exhausted():\n reason = error or ResponseError(cause)\n> raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]\nE urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError\n\nDuring handling of the above exception, another exception occurred:\n\nself = , msg_id = None\ntimeout = None\n\n def _check_if_message_on_server(self, msg_id, timeout=None):\n \"\"\"Try to load page for specific message. If there is a html tag like then there is no\n such message.\n \n :param msg_id: ID of message to be checked\n :params timeout: The value of timeout to be passed to the get request\n :return:\n \"\"\"\n \n request_headers = dict()\n if self._user or self._password:\n request_headers['Cookie'] = self._make_user_and_pswd_cookie()\n try:\n> response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False,\n verify=False, timeout=timeout)\n\nslic/utils/logbook.py:570: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:73: in get\n return request(\"get\", url, params=params, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request\n return session.request(method=method, url=url, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request\n resp = self.send(prep, **send_kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send\n r = adapter.send(request, **kwargs)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n \n except (ProtocolError, OSError) as err:\n raise ConnectionError(err, request=request)\n \n except MaxRetryError as e:\n if isinstance(e.reason, ConnectTimeoutError):\n # TODO: Remove this in 3.0.0: see #2811\n if not isinstance(e.reason, NewConnectionError):\n raise ConnectTimeout(e, request=request)\n \n if isinstance(e.reason, ResponseError):\n raise RetryError(e, request=request)\n \n if isinstance(e.reason, _ProxyError):\n raise ProxyError(e, request=request)\n \n if isinstance(e.reason, _SSLError):\n # This branch is for urllib3 v1.22 and later.\n raise SSLError(e, request=request)\n \n> raise ConnectionError(e, request=request)\nE requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError\n\nDuring handling of the above exception, another exception occurred:\n\nmock_home = \nmock_getpass = \n\n @patch(\"slic.utils.elog.getpass\")\n @patch(\"slic.utils.elog.Path.home\")\n def test_get_default_elog_instance_asks_password_and_opens(mock_home, mock_getpass):\n mock_home.return_value = Path(\"/does/not/exist\") # Fausse home \u2192 lecture \u00e9choue\n mock_getpass.return_value = \"testpassword\"\n user = \"robot\"\n text = \"This is a message2\"\n url = \"http://localhost:8080/demo\"\n \n elog = Elog(\"http://localhost:8080/demo\", user=user, encrypt_pwd=False)\n \n try:\n> elog.post(text)\n\ntests/test_utils_elog.py:59: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \nslic/utils/elog.py:17: in post\n return self._log.post(*args, **kwargs)\nslic/utils/logbook.py:289: in post\n self._check_if_message_on_server(msg_id) # Raises exceptions if no message or no response from server\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = , msg_id = None\ntimeout = None\n\n def _check_if_message_on_server(self, msg_id, timeout=None):\n \"\"\"Try to load page for specific message. If there is a html tag like then there is no\n such message.\n \n :param msg_id: ID of message to be checked\n :params timeout: The value of timeout to be passed to the get request\n :return:\n \"\"\"\n \n request_headers = dict()\n if self._user or self._password:\n request_headers['Cookie'] = self._make_user_and_pswd_cookie()\n try:\n response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False,\n verify=False, timeout=timeout)\n \n # If there is no message code 200 will be returned (OK) and _validate_response will not recognise it\n # but there will be some error in the html code.\n resp_message, resp_headers, resp_msg_id = _validate_response(response)\n # If there is no message, code 200 will be returned (OK) but there will be some error indication in\n # the html code.\n if re.findall('.*?',\n resp_message.decode('utf-8', 'ignore'),\n flags=re.DOTALL):\n raise LogbookInvalidMessageID('Message with ID: ' + str(msg_id) + ' does not exist on logbook.')\n \n except requests.Timeout as e:\n # Catch here a timeout o the post request.\n # Raise the logbook exception and let the user handle it\n raise LogbookServerTimeout('{0} method cannot be completed because of a network timeout:\\n' +\n '{1}'.format(sys._getframe().f_code.co_name, e))\n \n except requests.RequestException as e:\n> raise LogbookServerProblem('No response from the logbook server.\\nDetails: ' + '{0}'.format(e))\nE slic.utils.logbook_exceptions.LogbookServerProblem: No response from the logbook server.\nE Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\nslic/utils/logbook.py:590: LogbookServerProblem\n\nDuring handling of the above exception, another exception occurred:\n\nmock_home = \nmock_getpass = \n\n @patch(\"slic.utils.elog.getpass\")\n @patch(\"slic.utils.elog.Path.home\")\n def test_get_default_elog_instance_asks_password_and_opens(mock_home, mock_getpass):\n mock_home.return_value = Path(\"/does/not/exist\") # Fausse home \u2192 lecture \u00e9choue\n mock_getpass.return_value = \"testpassword\"\n user = \"robot\"\n text = \"This is a message2\"\n url = \"http://localhost:8080/demo\"\n \n elog = Elog(\"http://localhost:8080/demo\", user=user, encrypt_pwd=False)\n \n try:\n elog.post(text)\n except Exception as e:\n> pytest.fail(f\"elog.open() raised an unexpected exception: {e}\")\nE Failed: elog.open() raised an unexpected exception: No response from the logbook server.\nE Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\ntests/test_utils_elog.py:61: Failed"}, "teardown": {"duration": 0.0002443208359181881, "outcome": "passed"}}, {"nodeid": "tests/test_utils_elog.py::test_get_default_elog_with_path_home", "lineno": 72, "outcome": "failed", "keywords": ["test_get_default_elog_with_path_home", "__wrapped__", "patchings", "test_utils_elog.py", "tests", "slic", ""], "setup": {"duration": 0.00014232704415917397, "outcome": "passed"}, "call": {"duration": 0.006054379045963287, "outcome": "failed", "crash": {"path": "/workspace/tligui_y/slic/tests/test_utils_elog.py", "lineno": 95, "message": "Failed: elog.open() raised an unexpected exception: No response from the logbook server.\nDetails: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))"}, "traceback": [{"path": "tests/test_utils_elog.py", "lineno": 95, "message": "Failed"}], "longrepr": "self = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n> sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection\n raise err\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\naddress = ('localhost', 8080), timeout = None, source_address = None\nsocket_options = [(6, 1, 1)]\n\n def create_connection(\n address: tuple[str, int],\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n source_address: tuple[str, int] | None = None,\n socket_options: _TYPE_SOCKET_OPTIONS | None = None,\n ) -> socket.socket:\n \"\"\"Connect to *address* and return the socket object.\n \n Convenience function. Connect to *address* (a 2-tuple ``(host,\n port)``) and return the socket object. Passing the optional\n *timeout* parameter will set the timeout on the socket instance\n before attempting to connect. If no *timeout* is supplied, the\n global default timeout setting returned by :func:`socket.getdefaulttimeout`\n is used. If *source_address* is set it must be a tuple of (host, port)\n for the socket to bind as a source address before making the connection.\n An host of '' or port 0 tells the OS to use the default.\n \"\"\"\n \n host, port = address\n if host.startswith(\"[\"):\n host = host.strip(\"[]\")\n err = None\n \n # Using the value from allowed_gai_family() in the context of getaddrinfo lets\n # us select whether to work with IPv4 DNS records, IPv6 records, or both.\n # The original create_connection function always returns all records.\n family = allowed_gai_family()\n \n try:\n host.encode(\"idna\")\n except UnicodeError:\n raise LocationParseError(f\"'{host}', label empty or too long\") from None\n \n for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):\n af, socktype, proto, canonname, sa = res\n sock = None\n try:\n sock = socket.socket(af, socktype, proto)\n \n # If provided, set socket level options before connecting.\n _set_socket_options(sock, socket_options)\n \n if timeout is not _DEFAULT_TIMEOUT:\n sock.settimeout(timeout)\n if source_address:\n sock.bind(source_address)\n> sock.connect(sa)\nE ConnectionRefusedError: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nmethod = 'POST', url = '/demo/'\nbody = b'--9c8911db8eca06c93edc0908426a993c\\r\\nContent-Disposition: form-data; name=\"Author\"\\r\\n\\r\\nrobot\\r\\n--9c8911db8eca06...Disposition: form-data; name=\"Text\"; filename=\"\"\\r\\n\\r\\nThis is a message3\\r\\n--9c8911db8eca06c93edc0908426a993c--\\r\\n'\nheaders = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '705', 'Content-Type': 'multipart/form-data; boundary=9c8911db8eca06c93edc0908426a993c'}\nretries = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nredirect = False, assert_same_host = False\ntimeout = Timeout(connect=None, read=None, total=None), pool_timeout = None\nrelease_conn = False, chunked = False, body_pos = None, preload_content = False\ndecode_content = False, response_kw = {}\nparsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/', query=None, fragment=None)\ndestination_scheme = None, conn = None, release_this_conn = True\nhttp_tunnel_required = False, err = None, clean_exit = False\n\n def urlopen( # type: ignore[override]\n self,\n method: str,\n url: str,\n body: _TYPE_BODY | None = None,\n headers: typing.Mapping[str, str] | None = None,\n retries: Retry | bool | int | None = None,\n redirect: bool = True,\n assert_same_host: bool = True,\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n pool_timeout: int | None = None,\n release_conn: bool | None = None,\n chunked: bool = False,\n body_pos: _TYPE_BODY_POSITION | None = None,\n preload_content: bool = True,\n decode_content: bool = True,\n **response_kw: typing.Any,\n ) -> BaseHTTPResponse:\n \"\"\"\n Get a connection from the pool and perform an HTTP request. This is the\n lowest level call for making a request, so you'll need to specify all\n the raw details.\n \n .. note::\n \n More commonly, it's appropriate to use a convenience method\n such as :meth:`request`.\n \n .. note::\n \n `release_conn` will only behave as expected if\n `preload_content=False` because we want to make\n `preload_content=False` the default behaviour someday soon without\n breaking backwards compatibility.\n \n :param method:\n HTTP request method (such as GET, POST, PUT, etc.)\n \n :param url:\n The URL to perform the request on.\n \n :param body:\n Data to send in the request body, either :class:`str`, :class:`bytes`,\n an iterable of :class:`str`/:class:`bytes`, or a file-like object.\n \n :param headers:\n Dictionary of custom headers to send, such as User-Agent,\n If-None-Match, etc. If None, pool headers are used. If provided,\n these headers completely replace any pool-specific headers.\n \n :param retries:\n Configure the number of retries to allow before raising a\n :class:`~urllib3.exceptions.MaxRetryError` exception.\n \n If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a\n :class:`~urllib3.util.retry.Retry` object for fine-grained control\n over different types of retries.\n Pass an integer number to retry connection errors that many times,\n but no other types of errors. Pass zero to never retry.\n \n If ``False``, then retries are disabled and any exception is raised\n immediately. Also, instead of raising a MaxRetryError on redirects,\n the redirect response will be returned.\n \n :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int.\n \n :param redirect:\n If True, automatically handle redirects (status codes 301, 302,\n 303, 307, 308). Each redirect counts as a retry. Disabling retries\n will disable redirect, too.\n \n :param assert_same_host:\n If ``True``, will make sure that the host of the pool requests is\n consistent else will raise HostChangedError. When ``False``, you can\n use the pool on an HTTP proxy and request foreign hosts.\n \n :param timeout:\n If specified, overrides the default timeout for this one\n request. It may be a float (in seconds) or an instance of\n :class:`urllib3.util.Timeout`.\n \n :param pool_timeout:\n If set and the pool is set to block=True, then this method will\n block for ``pool_timeout`` seconds and raise EmptyPoolError if no\n connection is available within the time period.\n \n :param bool preload_content:\n If True, the response's body will be preloaded into memory.\n \n :param bool decode_content:\n If True, will attempt to decode the body based on the\n 'content-encoding' header.\n \n :param release_conn:\n If False, then the urlopen call will not release the connection\n back into the pool once a response is received (but will release if\n you read the entire contents of the response such as when\n `preload_content=True`). This is useful if you're not preloading\n the response's content immediately. You will need to call\n ``r.release_conn()`` on the response ``r`` to return the connection\n back into the pool. If None, it takes the value of ``preload_content``\n which defaults to ``True``.\n \n :param bool chunked:\n If True, urllib3 will send the body using chunked transfer\n encoding. Otherwise, urllib3 will send the body using the standard\n content-length form. Defaults to False.\n \n :param int body_pos:\n Position to seek to in file-like body in the event of a retry or\n redirect. Typically this won't need to be set because urllib3 will\n auto-populate the value when needed.\n \"\"\"\n parsed_url = parse_url(url)\n destination_scheme = parsed_url.scheme\n \n if headers is None:\n headers = self.headers\n \n if not isinstance(retries, Retry):\n retries = Retry.from_int(retries, redirect=redirect, default=self.retries)\n \n if release_conn is None:\n release_conn = preload_content\n \n # Check host\n if assert_same_host and not self.is_same_host(url):\n raise HostChangedError(self, url, retries)\n \n # Ensure that the URL we're connecting to is properly encoded\n if url.startswith(\"/\"):\n url = to_str(_encode_target(url))\n else:\n url = to_str(parsed_url.url)\n \n conn = None\n \n # Track whether `conn` needs to be released before\n # returning/raising/recursing. Update this variable if necessary, and\n # leave `release_conn` constant throughout the function. That way, if\n # the function recurses, the original value of `release_conn` will be\n # passed down into the recursive call, and its value will be respected.\n #\n # See issue #651 [1] for details.\n #\n # [1] \n release_this_conn = release_conn\n \n http_tunnel_required = connection_requires_http_tunnel(\n self.proxy, self.proxy_config, destination_scheme\n )\n \n # Merge the proxy headers. Only done when not using HTTP CONNECT. We\n # have to copy the headers dict so we can safely change it without those\n # changes being reflected in anyone else's copy.\n if not http_tunnel_required:\n headers = headers.copy() # type: ignore[attr-defined]\n headers.update(self.proxy_headers) # type: ignore[union-attr]\n \n # Must keep the exception bound to a separate variable or else Python 3\n # complains about UnboundLocalError.\n err = None\n \n # Keep track of whether we cleanly exited the except block. This\n # ensures we do proper cleanup in finally.\n clean_exit = False\n \n # Rewind body position, if needed. Record current position\n # for future rewinds in the event of a redirect/retry.\n body_pos = set_file_position(body, body_pos)\n \n try:\n # Request a connection from the queue.\n timeout_obj = self._get_timeout(timeout)\n conn = self._get_conn(timeout=pool_timeout)\n \n conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment]\n \n # Is this a closed/new connection that requires CONNECT tunnelling?\n if self.proxy is not None and http_tunnel_required and conn.is_closed:\n try:\n self._prepare_proxy(conn)\n except (BaseSSLError, OSError, SocketTimeout) as e:\n self._raise_timeout(\n err=e, url=self.proxy.url, timeout_value=conn.timeout\n )\n raise\n \n # If we're going to release the connection in ``finally:``, then\n # the response doesn't need to know about the connection. Otherwise\n # it will also try to release it and we'll have a double-release\n # mess.\n response_conn = conn if not release_conn else None\n \n # Make the request on the HTTPConnection object\n> response = self._make_request(\n conn,\n method,\n url,\n timeout=timeout_obj,\n body=body,\n headers=headers,\n chunked=chunked,\n retries=retries,\n response_conn=response_conn,\n preload_content=preload_content,\n decode_content=decode_content,\n **response_kw,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request\n conn.request(\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request\n self.endheaders()\n.pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders\n self._send_output(message_body, encode_chunked=encode_chunked)\n.pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output\n self.send(msg)\n.pixi/envs/default/lib/python3.8/http/client.py:951: in send\n self.connect()\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect\n self.sock = self._new_conn()\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n except socket.gaierror as e:\n raise NameResolutionError(self.host, self, e) from e\n except SocketTimeout as e:\n raise ConnectTimeoutError(\n self,\n f\"Connection to {self.host} timed out. (connect timeout={self.timeout})\",\n ) from e\n \n except OSError as e:\n> raise NewConnectionError(\n self, f\"Failed to establish a new connection: {e}\"\n ) from e\nE urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n> resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen\n retries = retries.increment(\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nmethod = 'POST', url = '/demo/', response = None\nerror = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')\n_pool = \n_stacktrace = \n\n def increment(\n self,\n method: str | None = None,\n url: str | None = None,\n response: BaseHTTPResponse | None = None,\n error: Exception | None = None,\n _pool: ConnectionPool | None = None,\n _stacktrace: TracebackType | None = None,\n ) -> Self:\n \"\"\"Return a new Retry object with incremented retry counters.\n \n :param response: A response object, or None, if the server did not\n return a response.\n :type response: :class:`~urllib3.response.BaseHTTPResponse`\n :param Exception error: An error encountered during the request, or\n None if the response was received successfully.\n \n :return: A new ``Retry`` object.\n \"\"\"\n if self.total is False and error:\n # Disabled, indicate to re-raise the error.\n raise reraise(type(error), error, _stacktrace)\n \n total = self.total\n if total is not None:\n total -= 1\n \n connect = self.connect\n read = self.read\n redirect = self.redirect\n status_count = self.status\n other = self.other\n cause = \"unknown\"\n status = None\n redirect_location = None\n \n if error and self._is_connection_error(error):\n # Connect retry?\n if connect is False:\n raise reraise(type(error), error, _stacktrace)\n elif connect is not None:\n connect -= 1\n \n elif error and self._is_read_error(error):\n # Read retry?\n if read is False or method is None or not self._is_method_retryable(method):\n raise reraise(type(error), error, _stacktrace)\n elif read is not None:\n read -= 1\n \n elif error:\n # Other retry?\n if other is not None:\n other -= 1\n \n elif response and response.get_redirect_location():\n # Redirect retry?\n if redirect is not None:\n redirect -= 1\n cause = \"too many redirects\"\n response_redirect_location = response.get_redirect_location()\n if response_redirect_location:\n redirect_location = response_redirect_location\n status = response.status\n \n else:\n # Incrementing because of a server error like a 500 in\n # status_forcelist and the given method is in the allowed_methods\n cause = ResponseError.GENERIC_ERROR\n if response and response.status:\n if status_count is not None:\n status_count -= 1\n cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status)\n status = response.status\n \n history = self.history + (\n RequestHistory(method, url, error, status, redirect_location),\n )\n \n new_retry = self.new(\n total=total,\n connect=connect,\n read=read,\n redirect=redirect,\n status=status_count,\n other=other,\n history=history,\n )\n \n if new_retry.is_exhausted():\n reason = error or ResponseError(cause)\n> raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]\nE urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError\n\nDuring handling of the above exception, another exception occurred:\n\nself = \nmessage = 'This is a message3', msg_id = None, reply = False\nattributes = {'Author': 'robot', 'When': 1755182071, 'cmd': 'Submit', 'exp': 'demo', ...}\nattachments = [], suppress_email_notification = False, encoding = None\ntimeout = None, kwargs = {'Author': 'robot'}\nlogbook_directory = 'elog_instance/logbooks/demo'\nnew_attachment_list = [('Text', ('', b'This is a message3'))]\nobjects_to_close = []\nattributes_to_edit = {'Author': b'robot', 'When': 1755182071, 'cmd': b'Submit', 'exp': b'demo', ...}\n\n def post(self, message, msg_id=None, reply=False, attributes=None, attachments=None,\n suppress_email_notification=False, encoding=None, timeout=None, **kwargs):\n \"\"\"\n Posts message to the logbook. If msg_id is not specified new message will be created, otherwise existing\n message will be edited, or a reply (if reply=True) to it will be created. This method returns the msg_id\n of the newly created message.\n \"\"\"\n \n logbook_directory = \"elog_instance/logbooks/demo\"\n print(f\"Checking the existence of the directory {logbook_directory}\")\n \n # Check if the directory exists\n if not os.path.exists(logbook_directory):\n print(f\"The directory {logbook_directory} does not exist.\")\n else:\n print(f\"The directory {logbook_directory} exists.\")\n \n # Check write permissions\n if os.access(logbook_directory, os.W_OK):\n print(f\"The directory {logbook_directory} has write permissions.\")\n else:\n print(f\"The directory {logbook_directory} does not have write permissions.\")\n \n \n print(\"STARTING POST\")\n # Ajout des impressions pour d\u00e9boguer\n print(f\"Message to post: {message}\")\n print(f\"msg_id: {msg_id}\")\n print(f\"Attributes: {attributes}\")\n print(f\"Attachments: {attachments}\")\n print(f\"Encoding: {encoding}\")\n print(f\"Timeout: {timeout}\")\n print(f\"Additional kwargs: {kwargs}\")\n \n attributes = attributes or {}\n attributes = {**attributes, **kwargs} # kwargs as attributes with higher priority\n print(f\"Updated attributes: {attributes}\")\n \n attachments = attachments or []\n print(f\"Attachments list: {attachments}\")\n \n if encoding is not None:\n if encoding not in ['plain', 'HTML', 'ELCode']:\n raise LogbookMessageRejected('Invalid message encoding. Valid options: plain, HTML, ELCode.')\n attributes['Encoding'] = encoding\n \n if suppress_email_notification:\n attributes[\"suppress\"] = 1\n \n # Prepare attachments\n if attachments:\n new_attachment_list, objects_to_close = self._prepare_attachments(attachments)\n print(f\"New attachments prepared: {new_attachment_list}\")\n else:\n objects_to_close = []\n new_attachment_list = []\n \n attributes_to_edit = dict()\n \n if msg_id:\n print(f\"Editing message with msg_id: {msg_id}\")\n if reply:\n print(f\"Replying to message with msg_id: {msg_id}\")\n self._check_if_message_on_server(msg_id)\n attributes['reply_to'] = str(msg_id)\n else:\n print(\"Editing existing message.\")\n attributes['edit_id'] = str(msg_id)\n attributes['skiplock'] = '1'\n msg_to_edit, attributes_to_edit, existing_attachments_list = self.read(msg_id)\n \n # Merge new attributes\n for attribute, data in attributes.items():\n if data is not None:\n attributes_to_edit[attribute] = data\n \n print(f\"Attributes after merging: {attributes_to_edit}\")\n \n # Process existing attachments\n i = 0\n existing_attachments_filename_list = []\n for attachment in existing_attachments_list:\n attributes_to_edit[f'attachment{i}'] = os.path.basename(attachment)\n existing_attachments_filename_list.append(os.path.basename(attachment)[14:])\n i += 1\n \n print(f\"Existing attachments: {existing_attachments_filename_list}\")\n \n duplicate_attachment_list = []\n for new_attachment in new_attachment_list:\n new_attachment_filename = new_attachment[1][0]\n print(f\"Checking new attachment: {new_attachment_filename}\")\n if new_attachment_filename in existing_attachments_filename_list:\n # Same attachment exists on the server, compare content\n new_attachment_content = new_attachment[1][1].read()\n new_attachment[1][1].seek(0)\n attachment_index = existing_attachments_filename_list.index(new_attachment_filename)\n existing_attachment_content = self.download_attachment(\n url=existing_attachments_list[attachment_index],\n timeout=timeout\n )\n if new_attachment_content == existing_attachment_content:\n print(f\"Duplicate attachment detected: {new_attachment_filename}\")\n duplicate_attachment_list.append(new_attachment)\n else:\n print(f\"Attachment content has changed: {new_attachment_filename}\")\n self.delete_attachment(msg_id, attributes=attributes_to_edit,\n attachment_id=attachment_index,\n timeout=timeout, text=msg_to_edit)\n existing_attachments_filename_list.pop(attachment_index)\n existing_attachments_list.pop(attachment_index)\n \n print(f\"Duplicate attachments to remove: {duplicate_attachment_list}\")\n \n # Remove duplicates\n for attach in duplicate_attachment_list:\n new_attachment_list.remove(attach)\n \n print(f\"Final new attachments list: {new_attachment_list}\")\n else:\n # Creating a new message, add timestamp if not present\n if 'When' not in attributes:\n attributes['When'] = int(datetime.now().timestamp())\n \n # Final check on attributes\n if not attributes_to_edit:\n attributes_to_edit = attributes\n \n print(f\"Final attributes to send to the server: {attributes_to_edit}\")\n \n # Remove reserved attributes\n _remove_reserved_attributes(attributes_to_edit)\n \n new_attachment_list.append(('Text', ('', message.encode('iso-8859-1'))))\n print(f\"Final attachment list including message text: {new_attachment_list}\")\n \n # Add base message attributes\n self._add_base_msg_attributes(attributes_to_edit)\n print(f\"Attributes with base message added: {attributes_to_edit}\")\n \n # Sanitize attribute keys\n attributes_to_edit = _replace_special_characters_in_attribute_keys(attributes_to_edit)\n print(f\"Attributes after sanitizing keys: {attributes_to_edit}\")\n \n # Encode all string values in latin1\n attributes_to_edit = _encode_values(attributes_to_edit)\n print(f\"Attributes after encoding: {attributes_to_edit}\")\n \n try:\n print(\"Sending POST request to the server...\")\n> response = requests.post(self._url, data=attributes_to_edit, files=new_attachment_list,\n allow_redirects=False, verify=False, timeout=timeout)\n\nslic/utils/logbook.py:269: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:115: in post\n return request(\"post\", url, data=data, json=json, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request\n return session.request(method=method, url=url, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request\n resp = self.send(prep, **send_kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send\n r = adapter.send(request, **kwargs)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n \n except (ProtocolError, OSError) as err:\n raise ConnectionError(err, request=request)\n \n except MaxRetryError as e:\n if isinstance(e.reason, ConnectTimeoutError):\n # TODO: Remove this in 3.0.0: see #2811\n if not isinstance(e.reason, NewConnectionError):\n raise ConnectTimeout(e, request=request)\n \n if isinstance(e.reason, ResponseError):\n raise RetryError(e, request=request)\n \n if isinstance(e.reason, _ProxyError):\n raise ProxyError(e, request=request)\n \n if isinstance(e.reason, _SSLError):\n # This branch is for urllib3 v1.22 and later.\n raise SSLError(e, request=request)\n \n> raise ConnectionError(e, request=request)\nE requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError\n\nDuring handling of the above exception, another exception occurred:\n\nself = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n> sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection\n raise err\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\naddress = ('localhost', 8080), timeout = None, source_address = None\nsocket_options = [(6, 1, 1)]\n\n def create_connection(\n address: tuple[str, int],\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n source_address: tuple[str, int] | None = None,\n socket_options: _TYPE_SOCKET_OPTIONS | None = None,\n ) -> socket.socket:\n \"\"\"Connect to *address* and return the socket object.\n \n Convenience function. Connect to *address* (a 2-tuple ``(host,\n port)``) and return the socket object. Passing the optional\n *timeout* parameter will set the timeout on the socket instance\n before attempting to connect. If no *timeout* is supplied, the\n global default timeout setting returned by :func:`socket.getdefaulttimeout`\n is used. If *source_address* is set it must be a tuple of (host, port)\n for the socket to bind as a source address before making the connection.\n An host of '' or port 0 tells the OS to use the default.\n \"\"\"\n \n host, port = address\n if host.startswith(\"[\"):\n host = host.strip(\"[]\")\n err = None\n \n # Using the value from allowed_gai_family() in the context of getaddrinfo lets\n # us select whether to work with IPv4 DNS records, IPv6 records, or both.\n # The original create_connection function always returns all records.\n family = allowed_gai_family()\n \n try:\n host.encode(\"idna\")\n except UnicodeError:\n raise LocationParseError(f\"'{host}', label empty or too long\") from None\n \n for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):\n af, socktype, proto, canonname, sa = res\n sock = None\n try:\n sock = socket.socket(af, socktype, proto)\n \n # If provided, set socket level options before connecting.\n _set_socket_options(sock, socket_options)\n \n if timeout is not _DEFAULT_TIMEOUT:\n sock.settimeout(timeout)\n if source_address:\n sock.bind(source_address)\n> sock.connect(sa)\nE ConnectionRefusedError: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nmethod = 'GET', url = '/demo/None', body = None\nheaders = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'unm=robot;upwd=testpassword;'}\nretries = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nredirect = False, assert_same_host = False\ntimeout = Timeout(connect=None, read=None, total=None), pool_timeout = None\nrelease_conn = False, chunked = False, body_pos = None, preload_content = False\ndecode_content = False, response_kw = {}\nparsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/None', query=None, fragment=None)\ndestination_scheme = None, conn = None, release_this_conn = True\nhttp_tunnel_required = False, err = None, clean_exit = False\n\n def urlopen( # type: ignore[override]\n self,\n method: str,\n url: str,\n body: _TYPE_BODY | None = None,\n headers: typing.Mapping[str, str] | None = None,\n retries: Retry | bool | int | None = None,\n redirect: bool = True,\n assert_same_host: bool = True,\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n pool_timeout: int | None = None,\n release_conn: bool | None = None,\n chunked: bool = False,\n body_pos: _TYPE_BODY_POSITION | None = None,\n preload_content: bool = True,\n decode_content: bool = True,\n **response_kw: typing.Any,\n ) -> BaseHTTPResponse:\n \"\"\"\n Get a connection from the pool and perform an HTTP request. This is the\n lowest level call for making a request, so you'll need to specify all\n the raw details.\n \n .. note::\n \n More commonly, it's appropriate to use a convenience method\n such as :meth:`request`.\n \n .. note::\n \n `release_conn` will only behave as expected if\n `preload_content=False` because we want to make\n `preload_content=False` the default behaviour someday soon without\n breaking backwards compatibility.\n \n :param method:\n HTTP request method (such as GET, POST, PUT, etc.)\n \n :param url:\n The URL to perform the request on.\n \n :param body:\n Data to send in the request body, either :class:`str`, :class:`bytes`,\n an iterable of :class:`str`/:class:`bytes`, or a file-like object.\n \n :param headers:\n Dictionary of custom headers to send, such as User-Agent,\n If-None-Match, etc. If None, pool headers are used. If provided,\n these headers completely replace any pool-specific headers.\n \n :param retries:\n Configure the number of retries to allow before raising a\n :class:`~urllib3.exceptions.MaxRetryError` exception.\n \n If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a\n :class:`~urllib3.util.retry.Retry` object for fine-grained control\n over different types of retries.\n Pass an integer number to retry connection errors that many times,\n but no other types of errors. Pass zero to never retry.\n \n If ``False``, then retries are disabled and any exception is raised\n immediately. Also, instead of raising a MaxRetryError on redirects,\n the redirect response will be returned.\n \n :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int.\n \n :param redirect:\n If True, automatically handle redirects (status codes 301, 302,\n 303, 307, 308). Each redirect counts as a retry. Disabling retries\n will disable redirect, too.\n \n :param assert_same_host:\n If ``True``, will make sure that the host of the pool requests is\n consistent else will raise HostChangedError. When ``False``, you can\n use the pool on an HTTP proxy and request foreign hosts.\n \n :param timeout:\n If specified, overrides the default timeout for this one\n request. It may be a float (in seconds) or an instance of\n :class:`urllib3.util.Timeout`.\n \n :param pool_timeout:\n If set and the pool is set to block=True, then this method will\n block for ``pool_timeout`` seconds and raise EmptyPoolError if no\n connection is available within the time period.\n \n :param bool preload_content:\n If True, the response's body will be preloaded into memory.\n \n :param bool decode_content:\n If True, will attempt to decode the body based on the\n 'content-encoding' header.\n \n :param release_conn:\n If False, then the urlopen call will not release the connection\n back into the pool once a response is received (but will release if\n you read the entire contents of the response such as when\n `preload_content=True`). This is useful if you're not preloading\n the response's content immediately. You will need to call\n ``r.release_conn()`` on the response ``r`` to return the connection\n back into the pool. If None, it takes the value of ``preload_content``\n which defaults to ``True``.\n \n :param bool chunked:\n If True, urllib3 will send the body using chunked transfer\n encoding. Otherwise, urllib3 will send the body using the standard\n content-length form. Defaults to False.\n \n :param int body_pos:\n Position to seek to in file-like body in the event of a retry or\n redirect. Typically this won't need to be set because urllib3 will\n auto-populate the value when needed.\n \"\"\"\n parsed_url = parse_url(url)\n destination_scheme = parsed_url.scheme\n \n if headers is None:\n headers = self.headers\n \n if not isinstance(retries, Retry):\n retries = Retry.from_int(retries, redirect=redirect, default=self.retries)\n \n if release_conn is None:\n release_conn = preload_content\n \n # Check host\n if assert_same_host and not self.is_same_host(url):\n raise HostChangedError(self, url, retries)\n \n # Ensure that the URL we're connecting to is properly encoded\n if url.startswith(\"/\"):\n url = to_str(_encode_target(url))\n else:\n url = to_str(parsed_url.url)\n \n conn = None\n \n # Track whether `conn` needs to be released before\n # returning/raising/recursing. Update this variable if necessary, and\n # leave `release_conn` constant throughout the function. That way, if\n # the function recurses, the original value of `release_conn` will be\n # passed down into the recursive call, and its value will be respected.\n #\n # See issue #651 [1] for details.\n #\n # [1] \n release_this_conn = release_conn\n \n http_tunnel_required = connection_requires_http_tunnel(\n self.proxy, self.proxy_config, destination_scheme\n )\n \n # Merge the proxy headers. Only done when not using HTTP CONNECT. We\n # have to copy the headers dict so we can safely change it without those\n # changes being reflected in anyone else's copy.\n if not http_tunnel_required:\n headers = headers.copy() # type: ignore[attr-defined]\n headers.update(self.proxy_headers) # type: ignore[union-attr]\n \n # Must keep the exception bound to a separate variable or else Python 3\n # complains about UnboundLocalError.\n err = None\n \n # Keep track of whether we cleanly exited the except block. This\n # ensures we do proper cleanup in finally.\n clean_exit = False\n \n # Rewind body position, if needed. Record current position\n # for future rewinds in the event of a redirect/retry.\n body_pos = set_file_position(body, body_pos)\n \n try:\n # Request a connection from the queue.\n timeout_obj = self._get_timeout(timeout)\n conn = self._get_conn(timeout=pool_timeout)\n \n conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment]\n \n # Is this a closed/new connection that requires CONNECT tunnelling?\n if self.proxy is not None and http_tunnel_required and conn.is_closed:\n try:\n self._prepare_proxy(conn)\n except (BaseSSLError, OSError, SocketTimeout) as e:\n self._raise_timeout(\n err=e, url=self.proxy.url, timeout_value=conn.timeout\n )\n raise\n \n # If we're going to release the connection in ``finally:``, then\n # the response doesn't need to know about the connection. Otherwise\n # it will also try to release it and we'll have a double-release\n # mess.\n response_conn = conn if not release_conn else None\n \n # Make the request on the HTTPConnection object\n> response = self._make_request(\n conn,\n method,\n url,\n timeout=timeout_obj,\n body=body,\n headers=headers,\n chunked=chunked,\n retries=retries,\n response_conn=response_conn,\n preload_content=preload_content,\n decode_content=decode_content,\n **response_kw,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request\n conn.request(\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request\n self.endheaders()\n.pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders\n self._send_output(message_body, encode_chunked=encode_chunked)\n.pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output\n self.send(msg)\n.pixi/envs/default/lib/python3.8/http/client.py:951: in send\n self.connect()\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect\n self.sock = self._new_conn()\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n except socket.gaierror as e:\n raise NameResolutionError(self.host, self, e) from e\n except SocketTimeout as e:\n raise ConnectTimeoutError(\n self,\n f\"Connection to {self.host} timed out. (connect timeout={self.timeout})\",\n ) from e\n \n except OSError as e:\n> raise NewConnectionError(\n self, f\"Failed to establish a new connection: {e}\"\n ) from e\nE urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n> resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen\n retries = retries.increment(\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nmethod = 'GET', url = '/demo/None', response = None\nerror = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')\n_pool = \n_stacktrace = \n\n def increment(\n self,\n method: str | None = None,\n url: str | None = None,\n response: BaseHTTPResponse | None = None,\n error: Exception | None = None,\n _pool: ConnectionPool | None = None,\n _stacktrace: TracebackType | None = None,\n ) -> Self:\n \"\"\"Return a new Retry object with incremented retry counters.\n \n :param response: A response object, or None, if the server did not\n return a response.\n :type response: :class:`~urllib3.response.BaseHTTPResponse`\n :param Exception error: An error encountered during the request, or\n None if the response was received successfully.\n \n :return: A new ``Retry`` object.\n \"\"\"\n if self.total is False and error:\n # Disabled, indicate to re-raise the error.\n raise reraise(type(error), error, _stacktrace)\n \n total = self.total\n if total is not None:\n total -= 1\n \n connect = self.connect\n read = self.read\n redirect = self.redirect\n status_count = self.status\n other = self.other\n cause = \"unknown\"\n status = None\n redirect_location = None\n \n if error and self._is_connection_error(error):\n # Connect retry?\n if connect is False:\n raise reraise(type(error), error, _stacktrace)\n elif connect is not None:\n connect -= 1\n \n elif error and self._is_read_error(error):\n # Read retry?\n if read is False or method is None or not self._is_method_retryable(method):\n raise reraise(type(error), error, _stacktrace)\n elif read is not None:\n read -= 1\n \n elif error:\n # Other retry?\n if other is not None:\n other -= 1\n \n elif response and response.get_redirect_location():\n # Redirect retry?\n if redirect is not None:\n redirect -= 1\n cause = \"too many redirects\"\n response_redirect_location = response.get_redirect_location()\n if response_redirect_location:\n redirect_location = response_redirect_location\n status = response.status\n \n else:\n # Incrementing because of a server error like a 500 in\n # status_forcelist and the given method is in the allowed_methods\n cause = ResponseError.GENERIC_ERROR\n if response and response.status:\n if status_count is not None:\n status_count -= 1\n cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status)\n status = response.status\n \n history = self.history + (\n RequestHistory(method, url, error, status, redirect_location),\n )\n \n new_retry = self.new(\n total=total,\n connect=connect,\n read=read,\n redirect=redirect,\n status=status_count,\n other=other,\n history=history,\n )\n \n if new_retry.is_exhausted():\n reason = error or ResponseError(cause)\n> raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]\nE urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError\n\nDuring handling of the above exception, another exception occurred:\n\nself = , msg_id = None\ntimeout = None\n\n def _check_if_message_on_server(self, msg_id, timeout=None):\n \"\"\"Try to load page for specific message. If there is a html tag like then there is no\n such message.\n \n :param msg_id: ID of message to be checked\n :params timeout: The value of timeout to be passed to the get request\n :return:\n \"\"\"\n \n request_headers = dict()\n if self._user or self._password:\n request_headers['Cookie'] = self._make_user_and_pswd_cookie()\n try:\n> response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False,\n verify=False, timeout=timeout)\n\nslic/utils/logbook.py:570: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:73: in get\n return request(\"get\", url, params=params, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request\n return session.request(method=method, url=url, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request\n resp = self.send(prep, **send_kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send\n r = adapter.send(request, **kwargs)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n \n except (ProtocolError, OSError) as err:\n raise ConnectionError(err, request=request)\n \n except MaxRetryError as e:\n if isinstance(e.reason, ConnectTimeoutError):\n # TODO: Remove this in 3.0.0: see #2811\n if not isinstance(e.reason, NewConnectionError):\n raise ConnectTimeout(e, request=request)\n \n if isinstance(e.reason, ResponseError):\n raise RetryError(e, request=request)\n \n if isinstance(e.reason, _ProxyError):\n raise ProxyError(e, request=request)\n \n if isinstance(e.reason, _SSLError):\n # This branch is for urllib3 v1.22 and later.\n raise SSLError(e, request=request)\n \n> raise ConnectionError(e, request=request)\nE requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError\n\nDuring handling of the above exception, another exception occurred:\n\nmock_home = \nmock_getuser = \nmock_getpass = \n\n @patch(\"slic.utils.elog.getpass\")\n @patch(\"slic.utils.elog.getuser\")\n @patch(\"slic.utils.elog.Path.home\")\n def test_get_default_elog_with_path_home(mock_home, mock_getuser, mock_getpass):\n fake_user = \"robot\"\n fake_pw = \"testpassword\"\n mock_getuser.return_value = fake_user\n mock_getpass.return_value = fake_pw # fallback safety\n text = \"This is a message3\"\n url = \"http://localhost:8080/demo\"\n \n tmp_home = Path(\"/tmp/fake_home_for_robot\")\n tmp_home.mkdir(parents=True, exist_ok=True)\n pw_file = tmp_home / \".elog_psi\"\n pw_file.write_text(fake_pw)\n mock_home.return_value = tmp_home\n \n try:\n elog = Elog(\"http://localhost:8080/demo\", encrypt_pwd=False)\n try:\n> elog.post(text)\n\ntests/test_utils_elog.py:93: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \nslic/utils/elog.py:17: in post\n return self._log.post(*args, **kwargs)\nslic/utils/logbook.py:289: in post\n self._check_if_message_on_server(msg_id) # Raises exceptions if no message or no response from server\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = , msg_id = None\ntimeout = None\n\n def _check_if_message_on_server(self, msg_id, timeout=None):\n \"\"\"Try to load page for specific message. If there is a html tag like then there is no\n such message.\n \n :param msg_id: ID of message to be checked\n :params timeout: The value of timeout to be passed to the get request\n :return:\n \"\"\"\n \n request_headers = dict()\n if self._user or self._password:\n request_headers['Cookie'] = self._make_user_and_pswd_cookie()\n try:\n response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False,\n verify=False, timeout=timeout)\n \n # If there is no message code 200 will be returned (OK) and _validate_response will not recognise it\n # but there will be some error in the html code.\n resp_message, resp_headers, resp_msg_id = _validate_response(response)\n # If there is no message, code 200 will be returned (OK) but there will be some error indication in\n # the html code.\n if re.findall('.*?',\n resp_message.decode('utf-8', 'ignore'),\n flags=re.DOTALL):\n raise LogbookInvalidMessageID('Message with ID: ' + str(msg_id) + ' does not exist on logbook.')\n \n except requests.Timeout as e:\n # Catch here a timeout o the post request.\n # Raise the logbook exception and let the user handle it\n raise LogbookServerTimeout('{0} method cannot be completed because of a network timeout:\\n' +\n '{1}'.format(sys._getframe().f_code.co_name, e))\n \n except requests.RequestException as e:\n> raise LogbookServerProblem('No response from the logbook server.\\nDetails: ' + '{0}'.format(e))\nE slic.utils.logbook_exceptions.LogbookServerProblem: No response from the logbook server.\nE Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\nslic/utils/logbook.py:590: LogbookServerProblem\n\nDuring handling of the above exception, another exception occurred:\n\nmock_home = \nmock_getuser = \nmock_getpass = \n\n @patch(\"slic.utils.elog.getpass\")\n @patch(\"slic.utils.elog.getuser\")\n @patch(\"slic.utils.elog.Path.home\")\n def test_get_default_elog_with_path_home(mock_home, mock_getuser, mock_getpass):\n fake_user = \"robot\"\n fake_pw = \"testpassword\"\n mock_getuser.return_value = fake_user\n mock_getpass.return_value = fake_pw # fallback safety\n text = \"This is a message3\"\n url = \"http://localhost:8080/demo\"\n \n tmp_home = Path(\"/tmp/fake_home_for_robot\")\n tmp_home.mkdir(parents=True, exist_ok=True)\n pw_file = tmp_home / \".elog_psi\"\n pw_file.write_text(fake_pw)\n mock_home.return_value = tmp_home\n \n try:\n elog = Elog(\"http://localhost:8080/demo\", encrypt_pwd=False)\n try:\n elog.post(text)\n except Exception as e:\n> pytest.fail(f\"elog.open() raised an unexpected exception: {e}\")\nE Failed: elog.open() raised an unexpected exception: No response from the logbook server.\nE Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\ntests/test_utils_elog.py:95: Failed"}, "teardown": {"duration": 0.00028157979249954224, "outcome": "passed"}}, {"nodeid": "tests/test_utils_elog.py::test_screenshot", "lineno": 121, "outcome": "failed", "keywords": ["test_screenshot", "__wrapped__", "patchings", "test_utils_elog.py", "tests", "slic", ""], "setup": {"duration": 0.00014507630839943886, "outcome": "passed"}, "call": {"duration": 0.007574480026960373, "outcome": "failed", "crash": {"path": "/workspace/tligui_y/slic/slic/utils/logbook.py", "lineno": 590, "message": "slic.utils.logbook_exceptions.LogbookServerProblem: No response from the logbook server.\nDetails: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))"}, "traceback": [{"path": "tests/test_utils_elog.py", "lineno": 134, "message": ""}, {"path": "slic/utils/elog.py", "lineno": 22, "message": "in screenshot"}, {"path": "slic/utils/elog.py", "lineno": 17, "message": "in post"}, {"path": "slic/utils/logbook.py", "lineno": 289, "message": "in post"}, {"path": "slic/utils/logbook.py", "lineno": 590, "message": "LogbookServerProblem"}], "longrepr": "self = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n> sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection\n raise err\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\naddress = ('localhost', 8080), timeout = None, source_address = None\nsocket_options = [(6, 1, 1)]\n\n def create_connection(\n address: tuple[str, int],\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n source_address: tuple[str, int] | None = None,\n socket_options: _TYPE_SOCKET_OPTIONS | None = None,\n ) -> socket.socket:\n \"\"\"Connect to *address* and return the socket object.\n \n Convenience function. Connect to *address* (a 2-tuple ``(host,\n port)``) and return the socket object. Passing the optional\n *timeout* parameter will set the timeout on the socket instance\n before attempting to connect. If no *timeout* is supplied, the\n global default timeout setting returned by :func:`socket.getdefaulttimeout`\n is used. If *source_address* is set it must be a tuple of (host, port)\n for the socket to bind as a source address before making the connection.\n An host of '' or port 0 tells the OS to use the default.\n \"\"\"\n \n host, port = address\n if host.startswith(\"[\"):\n host = host.strip(\"[]\")\n err = None\n \n # Using the value from allowed_gai_family() in the context of getaddrinfo lets\n # us select whether to work with IPv4 DNS records, IPv6 records, or both.\n # The original create_connection function always returns all records.\n family = allowed_gai_family()\n \n try:\n host.encode(\"idna\")\n except UnicodeError:\n raise LocationParseError(f\"'{host}', label empty or too long\") from None\n \n for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):\n af, socktype, proto, canonname, sa = res\n sock = None\n try:\n sock = socket.socket(af, socktype, proto)\n \n # If provided, set socket level options before connecting.\n _set_socket_options(sock, socket_options)\n \n if timeout is not _DEFAULT_TIMEOUT:\n sock.settimeout(timeout)\n if source_address:\n sock.bind(source_address)\n> sock.connect(sa)\nE ConnectionRefusedError: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nmethod = 'POST', url = '/demo/'\nbody = b'--5cc3f9217327db39fecbda82305dbfff\\r\\nContent-Disposition: form-data; name=\"Author\"\\r\\n\\r\\nrobot\\r\\n--5cc3f9217327db...-data; name=\"Text\"; filename=\"\"\\r\\n\\r\\nSCREENSHOT_INTEGRATION_TEST_MSG_456\\r\\n--5cc3f9217327db39fecbda82305dbfff--\\r\\n'\nheaders = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Content-Length': '858', 'Content-Type': 'multipart/form-data; boundary=5cc3f9217327db39fecbda82305dbfff'}\nretries = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nredirect = False, assert_same_host = False\ntimeout = Timeout(connect=None, read=None, total=None), pool_timeout = None\nrelease_conn = False, chunked = False, body_pos = None, preload_content = False\ndecode_content = False, response_kw = {}\nparsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/', query=None, fragment=None)\ndestination_scheme = None, conn = None, release_this_conn = True\nhttp_tunnel_required = False, err = None, clean_exit = False\n\n def urlopen( # type: ignore[override]\n self,\n method: str,\n url: str,\n body: _TYPE_BODY | None = None,\n headers: typing.Mapping[str, str] | None = None,\n retries: Retry | bool | int | None = None,\n redirect: bool = True,\n assert_same_host: bool = True,\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n pool_timeout: int | None = None,\n release_conn: bool | None = None,\n chunked: bool = False,\n body_pos: _TYPE_BODY_POSITION | None = None,\n preload_content: bool = True,\n decode_content: bool = True,\n **response_kw: typing.Any,\n ) -> BaseHTTPResponse:\n \"\"\"\n Get a connection from the pool and perform an HTTP request. This is the\n lowest level call for making a request, so you'll need to specify all\n the raw details.\n \n .. note::\n \n More commonly, it's appropriate to use a convenience method\n such as :meth:`request`.\n \n .. note::\n \n `release_conn` will only behave as expected if\n `preload_content=False` because we want to make\n `preload_content=False` the default behaviour someday soon without\n breaking backwards compatibility.\n \n :param method:\n HTTP request method (such as GET, POST, PUT, etc.)\n \n :param url:\n The URL to perform the request on.\n \n :param body:\n Data to send in the request body, either :class:`str`, :class:`bytes`,\n an iterable of :class:`str`/:class:`bytes`, or a file-like object.\n \n :param headers:\n Dictionary of custom headers to send, such as User-Agent,\n If-None-Match, etc. If None, pool headers are used. If provided,\n these headers completely replace any pool-specific headers.\n \n :param retries:\n Configure the number of retries to allow before raising a\n :class:`~urllib3.exceptions.MaxRetryError` exception.\n \n If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a\n :class:`~urllib3.util.retry.Retry` object for fine-grained control\n over different types of retries.\n Pass an integer number to retry connection errors that many times,\n but no other types of errors. Pass zero to never retry.\n \n If ``False``, then retries are disabled and any exception is raised\n immediately. Also, instead of raising a MaxRetryError on redirects,\n the redirect response will be returned.\n \n :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int.\n \n :param redirect:\n If True, automatically handle redirects (status codes 301, 302,\n 303, 307, 308). Each redirect counts as a retry. Disabling retries\n will disable redirect, too.\n \n :param assert_same_host:\n If ``True``, will make sure that the host of the pool requests is\n consistent else will raise HostChangedError. When ``False``, you can\n use the pool on an HTTP proxy and request foreign hosts.\n \n :param timeout:\n If specified, overrides the default timeout for this one\n request. It may be a float (in seconds) or an instance of\n :class:`urllib3.util.Timeout`.\n \n :param pool_timeout:\n If set and the pool is set to block=True, then this method will\n block for ``pool_timeout`` seconds and raise EmptyPoolError if no\n connection is available within the time period.\n \n :param bool preload_content:\n If True, the response's body will be preloaded into memory.\n \n :param bool decode_content:\n If True, will attempt to decode the body based on the\n 'content-encoding' header.\n \n :param release_conn:\n If False, then the urlopen call will not release the connection\n back into the pool once a response is received (but will release if\n you read the entire contents of the response such as when\n `preload_content=True`). This is useful if you're not preloading\n the response's content immediately. You will need to call\n ``r.release_conn()`` on the response ``r`` to return the connection\n back into the pool. If None, it takes the value of ``preload_content``\n which defaults to ``True``.\n \n :param bool chunked:\n If True, urllib3 will send the body using chunked transfer\n encoding. Otherwise, urllib3 will send the body using the standard\n content-length form. Defaults to False.\n \n :param int body_pos:\n Position to seek to in file-like body in the event of a retry or\n redirect. Typically this won't need to be set because urllib3 will\n auto-populate the value when needed.\n \"\"\"\n parsed_url = parse_url(url)\n destination_scheme = parsed_url.scheme\n \n if headers is None:\n headers = self.headers\n \n if not isinstance(retries, Retry):\n retries = Retry.from_int(retries, redirect=redirect, default=self.retries)\n \n if release_conn is None:\n release_conn = preload_content\n \n # Check host\n if assert_same_host and not self.is_same_host(url):\n raise HostChangedError(self, url, retries)\n \n # Ensure that the URL we're connecting to is properly encoded\n if url.startswith(\"/\"):\n url = to_str(_encode_target(url))\n else:\n url = to_str(parsed_url.url)\n \n conn = None\n \n # Track whether `conn` needs to be released before\n # returning/raising/recursing. Update this variable if necessary, and\n # leave `release_conn` constant throughout the function. That way, if\n # the function recurses, the original value of `release_conn` will be\n # passed down into the recursive call, and its value will be respected.\n #\n # See issue #651 [1] for details.\n #\n # [1] \n release_this_conn = release_conn\n \n http_tunnel_required = connection_requires_http_tunnel(\n self.proxy, self.proxy_config, destination_scheme\n )\n \n # Merge the proxy headers. Only done when not using HTTP CONNECT. We\n # have to copy the headers dict so we can safely change it without those\n # changes being reflected in anyone else's copy.\n if not http_tunnel_required:\n headers = headers.copy() # type: ignore[attr-defined]\n headers.update(self.proxy_headers) # type: ignore[union-attr]\n \n # Must keep the exception bound to a separate variable or else Python 3\n # complains about UnboundLocalError.\n err = None\n \n # Keep track of whether we cleanly exited the except block. This\n # ensures we do proper cleanup in finally.\n clean_exit = False\n \n # Rewind body position, if needed. Record current position\n # for future rewinds in the event of a redirect/retry.\n body_pos = set_file_position(body, body_pos)\n \n try:\n # Request a connection from the queue.\n timeout_obj = self._get_timeout(timeout)\n conn = self._get_conn(timeout=pool_timeout)\n \n conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment]\n \n # Is this a closed/new connection that requires CONNECT tunnelling?\n if self.proxy is not None and http_tunnel_required and conn.is_closed:\n try:\n self._prepare_proxy(conn)\n except (BaseSSLError, OSError, SocketTimeout) as e:\n self._raise_timeout(\n err=e, url=self.proxy.url, timeout_value=conn.timeout\n )\n raise\n \n # If we're going to release the connection in ``finally:``, then\n # the response doesn't need to know about the connection. Otherwise\n # it will also try to release it and we'll have a double-release\n # mess.\n response_conn = conn if not release_conn else None\n \n # Make the request on the HTTPConnection object\n> response = self._make_request(\n conn,\n method,\n url,\n timeout=timeout_obj,\n body=body,\n headers=headers,\n chunked=chunked,\n retries=retries,\n response_conn=response_conn,\n preload_content=preload_content,\n decode_content=decode_content,\n **response_kw,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request\n conn.request(\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request\n self.endheaders()\n.pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders\n self._send_output(message_body, encode_chunked=encode_chunked)\n.pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output\n self.send(msg)\n.pixi/envs/default/lib/python3.8/http/client.py:951: in send\n self.connect()\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect\n self.sock = self._new_conn()\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n except socket.gaierror as e:\n raise NameResolutionError(self.host, self, e) from e\n except SocketTimeout as e:\n raise ConnectTimeoutError(\n self,\n f\"Connection to {self.host} timed out. (connect timeout={self.timeout})\",\n ) from e\n \n except OSError as e:\n> raise NewConnectionError(\n self, f\"Failed to establish a new connection: {e}\"\n ) from e\nE urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n> resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen\n retries = retries.increment(\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nmethod = 'POST', url = '/demo/', response = None\nerror = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')\n_pool = \n_stacktrace = \n\n def increment(\n self,\n method: str | None = None,\n url: str | None = None,\n response: BaseHTTPResponse | None = None,\n error: Exception | None = None,\n _pool: ConnectionPool | None = None,\n _stacktrace: TracebackType | None = None,\n ) -> Self:\n \"\"\"Return a new Retry object with incremented retry counters.\n \n :param response: A response object, or None, if the server did not\n return a response.\n :type response: :class:`~urllib3.response.BaseHTTPResponse`\n :param Exception error: An error encountered during the request, or\n None if the response was received successfully.\n \n :return: A new ``Retry`` object.\n \"\"\"\n if self.total is False and error:\n # Disabled, indicate to re-raise the error.\n raise reraise(type(error), error, _stacktrace)\n \n total = self.total\n if total is not None:\n total -= 1\n \n connect = self.connect\n read = self.read\n redirect = self.redirect\n status_count = self.status\n other = self.other\n cause = \"unknown\"\n status = None\n redirect_location = None\n \n if error and self._is_connection_error(error):\n # Connect retry?\n if connect is False:\n raise reraise(type(error), error, _stacktrace)\n elif connect is not None:\n connect -= 1\n \n elif error and self._is_read_error(error):\n # Read retry?\n if read is False or method is None or not self._is_method_retryable(method):\n raise reraise(type(error), error, _stacktrace)\n elif read is not None:\n read -= 1\n \n elif error:\n # Other retry?\n if other is not None:\n other -= 1\n \n elif response and response.get_redirect_location():\n # Redirect retry?\n if redirect is not None:\n redirect -= 1\n cause = \"too many redirects\"\n response_redirect_location = response.get_redirect_location()\n if response_redirect_location:\n redirect_location = response_redirect_location\n status = response.status\n \n else:\n # Incrementing because of a server error like a 500 in\n # status_forcelist and the given method is in the allowed_methods\n cause = ResponseError.GENERIC_ERROR\n if response and response.status:\n if status_count is not None:\n status_count -= 1\n cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status)\n status = response.status\n \n history = self.history + (\n RequestHistory(method, url, error, status, redirect_location),\n )\n \n new_retry = self.new(\n total=total,\n connect=connect,\n read=read,\n redirect=redirect,\n status=status_count,\n other=other,\n history=history,\n )\n \n if new_retry.is_exhausted():\n reason = error or ResponseError(cause)\n> raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]\nE urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError\n\nDuring handling of the above exception, another exception occurred:\n\nself = \nmessage = 'SCREENSHOT_INTEGRATION_TEST_MSG_456', msg_id = None, reply = False\nattributes = {'Author': 'robot', 'When': 1755182071, 'cmd': 'Submit', 'exp': 'demo', ...}\nattachments = ['/tmp/fake_screenshot.png'], suppress_email_notification = False\nencoding = None, timeout = None, kwargs = {'Author': 'robot'}\nlogbook_directory = 'elog_instance/logbooks/demo'\nnew_attachment_list = [('attfile0', ('fake_screenshot.png', <_io.BufferedReader name='/tmp/fake_screenshot.png'>)), ('Text', ('', b'SCREENSHOT_INTEGRATION_TEST_MSG_456'))]\nobjects_to_close = [<_io.BufferedReader name='/tmp/fake_screenshot.png'>]\nattributes_to_edit = {'Author': b'robot', 'When': 1755182071, 'cmd': b'Submit', 'exp': b'demo', ...}\n\n def post(self, message, msg_id=None, reply=False, attributes=None, attachments=None,\n suppress_email_notification=False, encoding=None, timeout=None, **kwargs):\n \"\"\"\n Posts message to the logbook. If msg_id is not specified new message will be created, otherwise existing\n message will be edited, or a reply (if reply=True) to it will be created. This method returns the msg_id\n of the newly created message.\n \"\"\"\n \n logbook_directory = \"elog_instance/logbooks/demo\"\n print(f\"Checking the existence of the directory {logbook_directory}\")\n \n # Check if the directory exists\n if not os.path.exists(logbook_directory):\n print(f\"The directory {logbook_directory} does not exist.\")\n else:\n print(f\"The directory {logbook_directory} exists.\")\n \n # Check write permissions\n if os.access(logbook_directory, os.W_OK):\n print(f\"The directory {logbook_directory} has write permissions.\")\n else:\n print(f\"The directory {logbook_directory} does not have write permissions.\")\n \n \n print(\"STARTING POST\")\n # Ajout des impressions pour d\u00e9boguer\n print(f\"Message to post: {message}\")\n print(f\"msg_id: {msg_id}\")\n print(f\"Attributes: {attributes}\")\n print(f\"Attachments: {attachments}\")\n print(f\"Encoding: {encoding}\")\n print(f\"Timeout: {timeout}\")\n print(f\"Additional kwargs: {kwargs}\")\n \n attributes = attributes or {}\n attributes = {**attributes, **kwargs} # kwargs as attributes with higher priority\n print(f\"Updated attributes: {attributes}\")\n \n attachments = attachments or []\n print(f\"Attachments list: {attachments}\")\n \n if encoding is not None:\n if encoding not in ['plain', 'HTML', 'ELCode']:\n raise LogbookMessageRejected('Invalid message encoding. Valid options: plain, HTML, ELCode.')\n attributes['Encoding'] = encoding\n \n if suppress_email_notification:\n attributes[\"suppress\"] = 1\n \n # Prepare attachments\n if attachments:\n new_attachment_list, objects_to_close = self._prepare_attachments(attachments)\n print(f\"New attachments prepared: {new_attachment_list}\")\n else:\n objects_to_close = []\n new_attachment_list = []\n \n attributes_to_edit = dict()\n \n if msg_id:\n print(f\"Editing message with msg_id: {msg_id}\")\n if reply:\n print(f\"Replying to message with msg_id: {msg_id}\")\n self._check_if_message_on_server(msg_id)\n attributes['reply_to'] = str(msg_id)\n else:\n print(\"Editing existing message.\")\n attributes['edit_id'] = str(msg_id)\n attributes['skiplock'] = '1'\n msg_to_edit, attributes_to_edit, existing_attachments_list = self.read(msg_id)\n \n # Merge new attributes\n for attribute, data in attributes.items():\n if data is not None:\n attributes_to_edit[attribute] = data\n \n print(f\"Attributes after merging: {attributes_to_edit}\")\n \n # Process existing attachments\n i = 0\n existing_attachments_filename_list = []\n for attachment in existing_attachments_list:\n attributes_to_edit[f'attachment{i}'] = os.path.basename(attachment)\n existing_attachments_filename_list.append(os.path.basename(attachment)[14:])\n i += 1\n \n print(f\"Existing attachments: {existing_attachments_filename_list}\")\n \n duplicate_attachment_list = []\n for new_attachment in new_attachment_list:\n new_attachment_filename = new_attachment[1][0]\n print(f\"Checking new attachment: {new_attachment_filename}\")\n if new_attachment_filename in existing_attachments_filename_list:\n # Same attachment exists on the server, compare content\n new_attachment_content = new_attachment[1][1].read()\n new_attachment[1][1].seek(0)\n attachment_index = existing_attachments_filename_list.index(new_attachment_filename)\n existing_attachment_content = self.download_attachment(\n url=existing_attachments_list[attachment_index],\n timeout=timeout\n )\n if new_attachment_content == existing_attachment_content:\n print(f\"Duplicate attachment detected: {new_attachment_filename}\")\n duplicate_attachment_list.append(new_attachment)\n else:\n print(f\"Attachment content has changed: {new_attachment_filename}\")\n self.delete_attachment(msg_id, attributes=attributes_to_edit,\n attachment_id=attachment_index,\n timeout=timeout, text=msg_to_edit)\n existing_attachments_filename_list.pop(attachment_index)\n existing_attachments_list.pop(attachment_index)\n \n print(f\"Duplicate attachments to remove: {duplicate_attachment_list}\")\n \n # Remove duplicates\n for attach in duplicate_attachment_list:\n new_attachment_list.remove(attach)\n \n print(f\"Final new attachments list: {new_attachment_list}\")\n else:\n # Creating a new message, add timestamp if not present\n if 'When' not in attributes:\n attributes['When'] = int(datetime.now().timestamp())\n \n # Final check on attributes\n if not attributes_to_edit:\n attributes_to_edit = attributes\n \n print(f\"Final attributes to send to the server: {attributes_to_edit}\")\n \n # Remove reserved attributes\n _remove_reserved_attributes(attributes_to_edit)\n \n new_attachment_list.append(('Text', ('', message.encode('iso-8859-1'))))\n print(f\"Final attachment list including message text: {new_attachment_list}\")\n \n # Add base message attributes\n self._add_base_msg_attributes(attributes_to_edit)\n print(f\"Attributes with base message added: {attributes_to_edit}\")\n \n # Sanitize attribute keys\n attributes_to_edit = _replace_special_characters_in_attribute_keys(attributes_to_edit)\n print(f\"Attributes after sanitizing keys: {attributes_to_edit}\")\n \n # Encode all string values in latin1\n attributes_to_edit = _encode_values(attributes_to_edit)\n print(f\"Attributes after encoding: {attributes_to_edit}\")\n \n try:\n print(\"Sending POST request to the server...\")\n> response = requests.post(self._url, data=attributes_to_edit, files=new_attachment_list,\n allow_redirects=False, verify=False, timeout=timeout)\n\nslic/utils/logbook.py:269: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:115: in post\n return request(\"post\", url, data=data, json=json, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request\n return session.request(method=method, url=url, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request\n resp = self.send(prep, **send_kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send\n r = adapter.send(request, **kwargs)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n \n except (ProtocolError, OSError) as err:\n raise ConnectionError(err, request=request)\n \n except MaxRetryError as e:\n if isinstance(e.reason, ConnectTimeoutError):\n # TODO: Remove this in 3.0.0: see #2811\n if not isinstance(e.reason, NewConnectionError):\n raise ConnectTimeout(e, request=request)\n \n if isinstance(e.reason, ResponseError):\n raise RetryError(e, request=request)\n \n if isinstance(e.reason, _ProxyError):\n raise ProxyError(e, request=request)\n \n if isinstance(e.reason, _SSLError):\n # This branch is for urllib3 v1.22 and later.\n raise SSLError(e, request=request)\n \n> raise ConnectionError(e, request=request)\nE requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/ (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError\n\nDuring handling of the above exception, another exception occurred:\n\nself = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n> sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:199: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:85: in create_connection\n raise err\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\naddress = ('localhost', 8080), timeout = None, source_address = None\nsocket_options = [(6, 1, 1)]\n\n def create_connection(\n address: tuple[str, int],\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n source_address: tuple[str, int] | None = None,\n socket_options: _TYPE_SOCKET_OPTIONS | None = None,\n ) -> socket.socket:\n \"\"\"Connect to *address* and return the socket object.\n \n Convenience function. Connect to *address* (a 2-tuple ``(host,\n port)``) and return the socket object. Passing the optional\n *timeout* parameter will set the timeout on the socket instance\n before attempting to connect. If no *timeout* is supplied, the\n global default timeout setting returned by :func:`socket.getdefaulttimeout`\n is used. If *source_address* is set it must be a tuple of (host, port)\n for the socket to bind as a source address before making the connection.\n An host of '' or port 0 tells the OS to use the default.\n \"\"\"\n \n host, port = address\n if host.startswith(\"[\"):\n host = host.strip(\"[]\")\n err = None\n \n # Using the value from allowed_gai_family() in the context of getaddrinfo lets\n # us select whether to work with IPv4 DNS records, IPv6 records, or both.\n # The original create_connection function always returns all records.\n family = allowed_gai_family()\n \n try:\n host.encode(\"idna\")\n except UnicodeError:\n raise LocationParseError(f\"'{host}', label empty or too long\") from None\n \n for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):\n af, socktype, proto, canonname, sa = res\n sock = None\n try:\n sock = socket.socket(af, socktype, proto)\n \n # If provided, set socket level options before connecting.\n _set_socket_options(sock, socket_options)\n \n if timeout is not _DEFAULT_TIMEOUT:\n sock.settimeout(timeout)\n if source_address:\n sock.bind(source_address)\n> sock.connect(sa)\nE ConnectionRefusedError: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/connection.py:73: ConnectionRefusedError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nmethod = 'GET', url = '/demo/None', body = None\nheaders = {'User-Agent': 'python-requests/2.32.3', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive', 'Cookie': 'unm=robot;upwd=testpassword;'}\nretries = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nredirect = False, assert_same_host = False\ntimeout = Timeout(connect=None, read=None, total=None), pool_timeout = None\nrelease_conn = False, chunked = False, body_pos = None, preload_content = False\ndecode_content = False, response_kw = {}\nparsed_url = Url(scheme=None, auth=None, host=None, port=None, path='/demo/None', query=None, fragment=None)\ndestination_scheme = None, conn = None, release_this_conn = True\nhttp_tunnel_required = False, err = None, clean_exit = False\n\n def urlopen( # type: ignore[override]\n self,\n method: str,\n url: str,\n body: _TYPE_BODY | None = None,\n headers: typing.Mapping[str, str] | None = None,\n retries: Retry | bool | int | None = None,\n redirect: bool = True,\n assert_same_host: bool = True,\n timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT,\n pool_timeout: int | None = None,\n release_conn: bool | None = None,\n chunked: bool = False,\n body_pos: _TYPE_BODY_POSITION | None = None,\n preload_content: bool = True,\n decode_content: bool = True,\n **response_kw: typing.Any,\n ) -> BaseHTTPResponse:\n \"\"\"\n Get a connection from the pool and perform an HTTP request. This is the\n lowest level call for making a request, so you'll need to specify all\n the raw details.\n \n .. note::\n \n More commonly, it's appropriate to use a convenience method\n such as :meth:`request`.\n \n .. note::\n \n `release_conn` will only behave as expected if\n `preload_content=False` because we want to make\n `preload_content=False` the default behaviour someday soon without\n breaking backwards compatibility.\n \n :param method:\n HTTP request method (such as GET, POST, PUT, etc.)\n \n :param url:\n The URL to perform the request on.\n \n :param body:\n Data to send in the request body, either :class:`str`, :class:`bytes`,\n an iterable of :class:`str`/:class:`bytes`, or a file-like object.\n \n :param headers:\n Dictionary of custom headers to send, such as User-Agent,\n If-None-Match, etc. If None, pool headers are used. If provided,\n these headers completely replace any pool-specific headers.\n \n :param retries:\n Configure the number of retries to allow before raising a\n :class:`~urllib3.exceptions.MaxRetryError` exception.\n \n If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a\n :class:`~urllib3.util.retry.Retry` object for fine-grained control\n over different types of retries.\n Pass an integer number to retry connection errors that many times,\n but no other types of errors. Pass zero to never retry.\n \n If ``False``, then retries are disabled and any exception is raised\n immediately. Also, instead of raising a MaxRetryError on redirects,\n the redirect response will be returned.\n \n :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int.\n \n :param redirect:\n If True, automatically handle redirects (status codes 301, 302,\n 303, 307, 308). Each redirect counts as a retry. Disabling retries\n will disable redirect, too.\n \n :param assert_same_host:\n If ``True``, will make sure that the host of the pool requests is\n consistent else will raise HostChangedError. When ``False``, you can\n use the pool on an HTTP proxy and request foreign hosts.\n \n :param timeout:\n If specified, overrides the default timeout for this one\n request. It may be a float (in seconds) or an instance of\n :class:`urllib3.util.Timeout`.\n \n :param pool_timeout:\n If set and the pool is set to block=True, then this method will\n block for ``pool_timeout`` seconds and raise EmptyPoolError if no\n connection is available within the time period.\n \n :param bool preload_content:\n If True, the response's body will be preloaded into memory.\n \n :param bool decode_content:\n If True, will attempt to decode the body based on the\n 'content-encoding' header.\n \n :param release_conn:\n If False, then the urlopen call will not release the connection\n back into the pool once a response is received (but will release if\n you read the entire contents of the response such as when\n `preload_content=True`). This is useful if you're not preloading\n the response's content immediately. You will need to call\n ``r.release_conn()`` on the response ``r`` to return the connection\n back into the pool. If None, it takes the value of ``preload_content``\n which defaults to ``True``.\n \n :param bool chunked:\n If True, urllib3 will send the body using chunked transfer\n encoding. Otherwise, urllib3 will send the body using the standard\n content-length form. Defaults to False.\n \n :param int body_pos:\n Position to seek to in file-like body in the event of a retry or\n redirect. Typically this won't need to be set because urllib3 will\n auto-populate the value when needed.\n \"\"\"\n parsed_url = parse_url(url)\n destination_scheme = parsed_url.scheme\n \n if headers is None:\n headers = self.headers\n \n if not isinstance(retries, Retry):\n retries = Retry.from_int(retries, redirect=redirect, default=self.retries)\n \n if release_conn is None:\n release_conn = preload_content\n \n # Check host\n if assert_same_host and not self.is_same_host(url):\n raise HostChangedError(self, url, retries)\n \n # Ensure that the URL we're connecting to is properly encoded\n if url.startswith(\"/\"):\n url = to_str(_encode_target(url))\n else:\n url = to_str(parsed_url.url)\n \n conn = None\n \n # Track whether `conn` needs to be released before\n # returning/raising/recursing. Update this variable if necessary, and\n # leave `release_conn` constant throughout the function. That way, if\n # the function recurses, the original value of `release_conn` will be\n # passed down into the recursive call, and its value will be respected.\n #\n # See issue #651 [1] for details.\n #\n # [1] \n release_this_conn = release_conn\n \n http_tunnel_required = connection_requires_http_tunnel(\n self.proxy, self.proxy_config, destination_scheme\n )\n \n # Merge the proxy headers. Only done when not using HTTP CONNECT. We\n # have to copy the headers dict so we can safely change it without those\n # changes being reflected in anyone else's copy.\n if not http_tunnel_required:\n headers = headers.copy() # type: ignore[attr-defined]\n headers.update(self.proxy_headers) # type: ignore[union-attr]\n \n # Must keep the exception bound to a separate variable or else Python 3\n # complains about UnboundLocalError.\n err = None\n \n # Keep track of whether we cleanly exited the except block. This\n # ensures we do proper cleanup in finally.\n clean_exit = False\n \n # Rewind body position, if needed. Record current position\n # for future rewinds in the event of a redirect/retry.\n body_pos = set_file_position(body, body_pos)\n \n try:\n # Request a connection from the queue.\n timeout_obj = self._get_timeout(timeout)\n conn = self._get_conn(timeout=pool_timeout)\n \n conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment]\n \n # Is this a closed/new connection that requires CONNECT tunnelling?\n if self.proxy is not None and http_tunnel_required and conn.is_closed:\n try:\n self._prepare_proxy(conn)\n except (BaseSSLError, OSError, SocketTimeout) as e:\n self._raise_timeout(\n err=e, url=self.proxy.url, timeout_value=conn.timeout\n )\n raise\n \n # If we're going to release the connection in ``finally:``, then\n # the response doesn't need to know about the connection. Otherwise\n # it will also try to release it and we'll have a double-release\n # mess.\n response_conn = conn if not release_conn else None\n \n # Make the request on the HTTPConnection object\n> response = self._make_request(\n conn,\n method,\n url,\n timeout=timeout_obj,\n body=body,\n headers=headers,\n chunked=chunked,\n retries=retries,\n response_conn=response_conn,\n preload_content=preload_content,\n decode_content=decode_content,\n **response_kw,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:789: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:495: in _make_request\n conn.request(\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:441: in request\n self.endheaders()\n.pixi/envs/default/lib/python3.8/http/client.py:1251: in endheaders\n self._send_output(message_body, encode_chunked=encode_chunked)\n.pixi/envs/default/lib/python3.8/http/client.py:1011: in _send_output\n self.send(msg)\n.pixi/envs/default/lib/python3.8/http/client.py:951: in send\n self.connect()\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:279: in connect\n self.sock = self._new_conn()\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \n\n def _new_conn(self) -> socket.socket:\n \"\"\"Establish a socket connection and set nodelay settings on it.\n \n :return: New socket connection.\n \"\"\"\n try:\n sock = connection.create_connection(\n (self._dns_host, self.port),\n self.timeout,\n source_address=self.source_address,\n socket_options=self.socket_options,\n )\n except socket.gaierror as e:\n raise NameResolutionError(self.host, self, e) from e\n except SocketTimeout as e:\n raise ConnectTimeoutError(\n self,\n f\"Connection to {self.host} timed out. (connect timeout={self.timeout})\",\n ) from e\n \n except OSError as e:\n> raise NewConnectionError(\n self, f\"Failed to establish a new connection: {e}\"\n ) from e\nE urllib3.exceptions.NewConnectionError: : Failed to establish a new connection: [Errno 111] Connection refused\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connection.py:214: NewConnectionError\n\nThe above exception was the direct cause of the following exception:\n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n> resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:667: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/urllib3/connectionpool.py:843: in urlopen\n retries = retries.increment(\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = Retry(total=0, connect=None, read=False, redirect=None, status=None)\nmethod = 'GET', url = '/demo/None', response = None\nerror = NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused')\n_pool = \n_stacktrace = \n\n def increment(\n self,\n method: str | None = None,\n url: str | None = None,\n response: BaseHTTPResponse | None = None,\n error: Exception | None = None,\n _pool: ConnectionPool | None = None,\n _stacktrace: TracebackType | None = None,\n ) -> Self:\n \"\"\"Return a new Retry object with incremented retry counters.\n \n :param response: A response object, or None, if the server did not\n return a response.\n :type response: :class:`~urllib3.response.BaseHTTPResponse`\n :param Exception error: An error encountered during the request, or\n None if the response was received successfully.\n \n :return: A new ``Retry`` object.\n \"\"\"\n if self.total is False and error:\n # Disabled, indicate to re-raise the error.\n raise reraise(type(error), error, _stacktrace)\n \n total = self.total\n if total is not None:\n total -= 1\n \n connect = self.connect\n read = self.read\n redirect = self.redirect\n status_count = self.status\n other = self.other\n cause = \"unknown\"\n status = None\n redirect_location = None\n \n if error and self._is_connection_error(error):\n # Connect retry?\n if connect is False:\n raise reraise(type(error), error, _stacktrace)\n elif connect is not None:\n connect -= 1\n \n elif error and self._is_read_error(error):\n # Read retry?\n if read is False or method is None or not self._is_method_retryable(method):\n raise reraise(type(error), error, _stacktrace)\n elif read is not None:\n read -= 1\n \n elif error:\n # Other retry?\n if other is not None:\n other -= 1\n \n elif response and response.get_redirect_location():\n # Redirect retry?\n if redirect is not None:\n redirect -= 1\n cause = \"too many redirects\"\n response_redirect_location = response.get_redirect_location()\n if response_redirect_location:\n redirect_location = response_redirect_location\n status = response.status\n \n else:\n # Incrementing because of a server error like a 500 in\n # status_forcelist and the given method is in the allowed_methods\n cause = ResponseError.GENERIC_ERROR\n if response and response.status:\n if status_count is not None:\n status_count -= 1\n cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status)\n status = response.status\n \n history = self.history + (\n RequestHistory(method, url, error, status, redirect_location),\n )\n \n new_retry = self.new(\n total=total,\n connect=connect,\n read=read,\n redirect=redirect,\n status=status_count,\n other=other,\n history=history,\n )\n \n if new_retry.is_exhausted():\n reason = error or ResponseError(cause)\n> raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]\nE urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/urllib3/util/retry.py:519: MaxRetryError\n\nDuring handling of the above exception, another exception occurred:\n\nself = , msg_id = None\ntimeout = None\n\n def _check_if_message_on_server(self, msg_id, timeout=None):\n \"\"\"Try to load page for specific message. If there is a html tag like then there is no\n such message.\n \n :param msg_id: ID of message to be checked\n :params timeout: The value of timeout to be passed to the get request\n :return:\n \"\"\"\n \n request_headers = dict()\n if self._user or self._password:\n request_headers['Cookie'] = self._make_user_and_pswd_cookie()\n try:\n> response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False,\n verify=False, timeout=timeout)\n\nslic/utils/logbook.py:570: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:73: in get\n return request(\"get\", url, params=params, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/api.py:59: in request\n return session.request(method=method, url=url, **kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:589: in request\n resp = self.send(prep, **send_kwargs)\n.pixi/envs/default/lib/python3.8/site-packages/requests/sessions.py:703: in send\n r = adapter.send(request, **kwargs)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = \nrequest = , stream = False\ntimeout = Timeout(connect=None, read=None, total=None), verify = False\ncert = None, proxies = OrderedDict()\n\n def send(\n self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None\n ):\n \"\"\"Sends PreparedRequest object. Returns Response object.\n \n :param request: The :class:`PreparedRequest ` being sent.\n :param stream: (optional) Whether to stream the request content.\n :param timeout: (optional) How long to wait for the server to send\n data before giving up, as a float, or a :ref:`(connect timeout,\n read timeout) ` tuple.\n :type timeout: float or tuple or urllib3 Timeout object\n :param verify: (optional) Either a boolean, in which case it controls whether\n we verify the server's TLS certificate, or a string, in which case it\n must be a path to a CA bundle to use\n :param cert: (optional) Any user-provided SSL certificate to be trusted.\n :param proxies: (optional) The proxies dictionary to apply to the request.\n :rtype: requests.Response\n \"\"\"\n \n try:\n conn = self.get_connection_with_tls_context(\n request, verify, proxies=proxies, cert=cert\n )\n except LocationValueError as e:\n raise InvalidURL(e, request=request)\n \n self.cert_verify(conn, request.url, verify, cert)\n url = self.request_url(request, proxies)\n self.add_headers(\n request,\n stream=stream,\n timeout=timeout,\n verify=verify,\n cert=cert,\n proxies=proxies,\n )\n \n chunked = not (request.body is None or \"Content-Length\" in request.headers)\n \n if isinstance(timeout, tuple):\n try:\n connect, read = timeout\n timeout = TimeoutSauce(connect=connect, read=read)\n except ValueError:\n raise ValueError(\n f\"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, \"\n f\"or a single float to set both timeouts to the same value.\"\n )\n elif isinstance(timeout, TimeoutSauce):\n pass\n else:\n timeout = TimeoutSauce(connect=timeout, read=timeout)\n \n try:\n resp = conn.urlopen(\n method=request.method,\n url=url,\n body=request.body,\n headers=request.headers,\n redirect=False,\n assert_same_host=False,\n preload_content=False,\n decode_content=False,\n retries=self.max_retries,\n timeout=timeout,\n chunked=chunked,\n )\n \n except (ProtocolError, OSError) as err:\n raise ConnectionError(err, request=request)\n \n except MaxRetryError as e:\n if isinstance(e.reason, ConnectTimeoutError):\n # TODO: Remove this in 3.0.0: see #2811\n if not isinstance(e.reason, NewConnectionError):\n raise ConnectTimeout(e, request=request)\n \n if isinstance(e.reason, ResponseError):\n raise RetryError(e, request=request)\n \n if isinstance(e.reason, _ProxyError):\n raise ProxyError(e, request=request)\n \n if isinstance(e.reason, _SSLError):\n # This branch is for urllib3 v1.22 and later.\n raise SSLError(e, request=request)\n \n> raise ConnectionError(e, request=request)\nE requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\n.pixi/envs/default/lib/python3.8/site-packages/requests/adapters.py:700: ConnectionError\n\nDuring handling of the above exception, another exception occurred:\n\nmock_screenshot_class = \n\n @patch(\"slic.utils.elog.Screenshot\")\n def test_screenshot(mock_screenshot_class):\n fake_path = \"/tmp/fake_screenshot.png\"\n with open(fake_path, \"wb\") as f:\n f.write(b\"fake image data\")\n \n mock_instance = mock_screenshot_class.return_value\n mock_instance.shoot.return_value = [fake_path]\n \n elog = elog = Elog(\"http://localhost:8080/demo\", user=\"robot\", password=\"testpassword\", encrypt_pwd=False)\n \n test_msg = \"SCREENSHOT_INTEGRATION_TEST_MSG_456\"\n> elog.screenshot(message=test_msg)\n\ntests/test_utils_elog.py:134: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \nslic/utils/elog.py:22: in screenshot\n self.post(message, **kwargs)\nslic/utils/elog.py:17: in post\n return self._log.post(*args, **kwargs)\nslic/utils/logbook.py:289: in post\n self._check_if_message_on_server(msg_id) # Raises exceptions if no message or no response from server\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = , msg_id = None\ntimeout = None\n\n def _check_if_message_on_server(self, msg_id, timeout=None):\n \"\"\"Try to load page for specific message. If there is a html tag like then there is no\n such message.\n \n :param msg_id: ID of message to be checked\n :params timeout: The value of timeout to be passed to the get request\n :return:\n \"\"\"\n \n request_headers = dict()\n if self._user or self._password:\n request_headers['Cookie'] = self._make_user_and_pswd_cookie()\n try:\n response = requests.get(self._url + str(msg_id), headers=request_headers, allow_redirects=False,\n verify=False, timeout=timeout)\n \n # If there is no message code 200 will be returned (OK) and _validate_response will not recognise it\n # but there will be some error in the html code.\n resp_message, resp_headers, resp_msg_id = _validate_response(response)\n # If there is no message, code 200 will be returned (OK) but there will be some error indication in\n # the html code.\n if re.findall('.*?',\n resp_message.decode('utf-8', 'ignore'),\n flags=re.DOTALL):\n raise LogbookInvalidMessageID('Message with ID: ' + str(msg_id) + ' does not exist on logbook.')\n \n except requests.Timeout as e:\n # Catch here a timeout o the post request.\n # Raise the logbook exception and let the user handle it\n raise LogbookServerTimeout('{0} method cannot be completed because of a network timeout:\\n' +\n '{1}'.format(sys._getframe().f_code.co_name, e))\n \n except requests.RequestException as e:\n> raise LogbookServerProblem('No response from the logbook server.\\nDetails: ' + '{0}'.format(e))\nE slic.utils.logbook_exceptions.LogbookServerProblem: No response from the logbook server.\nE Details: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /demo/None (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused'))\n\nslic/utils/logbook.py:590: LogbookServerProblem"}, "teardown": {"duration": 0.00026907073333859444, "outcome": "passed"}}], "warnings": [{"message": "invalid escape sequence \\-", "category": "DeprecationWarning", "when": "collect", "filename": "/workspace/tligui_y/slic/.pixi/envs/default/lib/python3.8/site-packages/bsread/h5.py", "lineno": 207}, {"message": "The module numpy.dual is deprecated. Instead of using dual, use the functions directly from numpy or scipy.", "category": "DeprecationWarning", "when": "collect", "filename": "/workspace/tligui_y/slic/.pixi/envs/default/lib/python3.8/site-packages/scipy/fft/__init__.py", "lineno": 97}]} \ No newline at end of file