andreroggeri / pynubank

Acesse seus extratos do Nubank pelo Python
MIT License
1.19k stars 180 forks source link

[MacOS] Cannot allocate write+execute memory for ffi.callback(). You might be running on a system that prevents this. #369

Closed WittmannF closed 1 year ago

WittmannF commented 1 year ago

Estou tentando rodar o seguinte comando no meu mac:

nu.authenticate_with_cert(...)

Mas esta retornando o seguinte erro:

---------------------------------------------------------------------------
MemoryError                               Traceback (most recent call last)
Input In [7], in <cell line: 1>()
----> 1 nu.authenticate_with_cert(
...
      5             )

File ~/miniforge3/lib/python3.9/site-packages/pynubank/nubank.py:118, in Nubank.authenticate_with_cert(self, cpf, password, cert_path)
    109 url = self._discovery.get_app_url('token')
    110 payload = {
    111     'grant_type': 'password',
    112     'client_id': 'legacy_client_id',
   (...)
    115     'password': password
    116 }
--> 118 response = self._client.post(url, json=payload)
    120 self._save_auth_data(response)
    121 self._auth_mode = AuthMode.APP

File ~/miniforge3/lib/python3.9/site-packages/pynubank/utils/http.py:46, in HttpClient.post(self, url, json)
     45 def post(self, url: str, json: dict) -> dict:
---> 46     return self._handle_response(post(url, json=json, headers=self._headers, **self._cert_args))

File ~/miniforge3/lib/python3.9/site-packages/requests_pkcs12.py:157, in post(*args, **kwargs)
    156 def post(*args, **kwargs):
--> 157     return request('post', *args, **kwargs)

File ~/miniforge3/lib/python3.9/site-packages/requests_pkcs12.py:136, in request(*args, **kwargs)
    129 pkcs12_adapter = Pkcs12Adapter(
    130     pkcs12_data=pkcs12_data,
    131     pkcs12_filename=pkcs12_filename,
    132     pkcs12_password=pkcs12_password,
    133     ssl_protocol=ssl_protocol,
    134 )
    135 session.mount('https://', pkcs12_adapter)
--> 136 return session.request(*args, **kwargs)

File ~/miniforge3/lib/python3.9/site-packages/requests/sessions.py:587, in Session.request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)
    582 send_kwargs = {
    583     "timeout": timeout,
    584     "allow_redirects": allow_redirects,
    585 }
    586 send_kwargs.update(settings)
--> 587 resp = self.send(prep, **send_kwargs)
    589 return resp

File ~/miniforge3/lib/python3.9/site-packages/requests/sessions.py:701, in Session.send(self, request, **kwargs)
    698 start = preferred_clock()
    700 # Send the request
--> 701 r = adapter.send(request, **kwargs)
    703 # Total elapsed time of the request (approximately)
    704 elapsed = preferred_clock() - start

File ~/miniforge3/lib/python3.9/site-packages/requests/adapters.py:489, in HTTPAdapter.send(self, request, stream, timeout, verify, cert, proxies)
    487 try:
    488     if not chunked:
--> 489         resp = conn.urlopen(
    490             method=request.method,
    491             url=url,
    492             body=request.body,
    493             headers=request.headers,
    494             redirect=False,
    495             assert_same_host=False,
    496             preload_content=False,
    497             decode_content=False,
    498             retries=self.max_retries,
    499             timeout=timeout,
    500         )
    502     # Send the request.
    503     else:
    504         if hasattr(conn, "proxy_pool"):

File ~/miniforge3/lib/python3.9/site-packages/urllib3/connectionpool.py:703, in HTTPConnectionPool.urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
    700     self._prepare_proxy(conn)
    702 # Make the request on the httplib connection object.
--> 703 httplib_response = self._make_request(
    704     conn,
    705     method,
    706     url,
    707     timeout=timeout_obj,
    708     body=body,
    709     headers=headers,
    710     chunked=chunked,
    711 )
    713 # If we're going to release the connection in ``finally:``, then
    714 # the response doesn't need to know about the connection. Otherwise
    715 # it will also try to release it and we'll have a double-release
    716 # mess.
    717 response_conn = conn if not release_conn else None

File ~/miniforge3/lib/python3.9/site-packages/urllib3/connectionpool.py:386, in HTTPConnectionPool._make_request(self, conn, method, url, timeout, chunked, **httplib_request_kw)
    384 # Trigger any extra validation we need to do.
    385 try:
--> 386     self._validate_conn(conn)
    387 except (SocketTimeout, BaseSSLError) as e:
    388     # Py2 raises this as a BaseSSLError, Py3 raises it as socket timeout.
    389     self._raise_timeout(err=e, url=url, timeout_value=conn.timeout)

File ~/miniforge3/lib/python3.9/site-packages/urllib3/connectionpool.py:1042, in HTTPSConnectionPool._validate_conn(self, conn)
   1040 # Force connect early to allow us to validate the connection.
   1041 if not getattr(conn, "sock", None):  # AppEngine might not have  `.sock`
-> 1042     conn.connect()
   1044 if not conn.is_verified:
   1045     warnings.warn(
   1046         (
   1047             "Unverified HTTPS request is being made to host '%s'. "
   (...)
   1052         InsecureRequestWarning,
   1053     )

File ~/miniforge3/lib/python3.9/site-packages/urllib3/connection.py:401, in HTTPSConnection.connect(self)
    395     self.ssl_context = create_urllib3_context(
    396         ssl_version=resolve_ssl_version(self.ssl_version),
    397         cert_reqs=resolve_cert_reqs(self.cert_reqs),
    398     )
    400 context = self.ssl_context
--> 401 context.verify_mode = resolve_cert_reqs(self.cert_reqs)
    403 # Try to load OS default certs if none are given.
    404 # Works well on Windows (requires Python3.4+)
    405 if (
    406     not self.ca_certs
    407     and not self.ca_cert_dir
   (...)
    410     and hasattr(context, "load_default_certs")
    411 ):

File ~/miniforge3/lib/python3.9/site-packages/urllib3/contrib/pyopenssl.py:443, in PyOpenSSLContext.verify_mode(self, value)
    441 @verify_mode.setter
    442 def verify_mode(self, value):
--> 443     self._ctx.set_verify(_stdlib_to_openssl_verify[value], _verify_callback)

File ~/miniforge3/lib/python3.9/site-packages/OpenSSL/SSL.py:1128, in Context.set_verify(self, mode, callback)
   1125 if not callable(callback):
   1126     raise TypeError("callback must be callable")
-> 1128 self._verify_helper = _VerifyHelper(callback)
   1129 self._verify_callback = self._verify_helper.callback
   1130 _lib.SSL_CTX_set_verify(self._context, mode, self._verify_callback)

File ~/miniforge3/lib/python3.9/site-packages/OpenSSL/SSL.py:359, in _VerifyHelper.__init__(self, callback)
    356         else:
    357             return 0
--> 359 self.callback = _ffi.callback(
    360     "int (*)(int, X509_STORE_CTX *)", wrapper
    361 )

O erro parece relacionado ao pyOpenSSL, mas testei todas as sugestões de lá e ainda não funciona.

andreroggeri commented 1 year ago

Puts, não sei muito como ajudar.

Acho que tenho o mesmo mac que você e não tenho problemas.

Você poderia tentar rodar o seu código dentro de um container docker pra ver o que acontece?

algo como

# Assumindo que na pasta atual tenha uma pasta `key` com o seu certificado
$ docker run --rm -it -v $(pwd)/key:/key python:3.9 bash
$ pip install pynubank
$ python
> from pynubank import Nubank
> nu = Nubank()
> nu.authenticate_with_cert('CPF', 'SENHA', '/key/cert.p12')
WittmannF commented 1 year ago

Via docker funcionou @andreroggeri ! Agradeço por compartilhar rapidamente os passos. Quanto ao problema com pyopenssl, é um issue aberto há 3 anos, portanto não parece trivial. Mas achei curioso que no seu Mac está funcionando. O meu é Pro 2021 M1 rodando MacOS Monterey. Para ambiente em python utilizo conda miniforge.

WittmannF commented 1 year ago

Opa, para atualizar, consegui resolver instalando cffi via pip: pip install cffi

A versão em conda ainda não possui o patch que resolve a questão do pyOpenSSL