empyrebase 2.3.2__tar.gz → 2.3.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. {empyrebase-2.3.2/empyrebase.egg-info → empyrebase-2.3.4}/PKG-INFO +19 -3
  2. {empyrebase-2.3.2 → empyrebase-2.3.4}/README.md +18 -2
  3. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/empyrebase.py +1 -1
  4. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/services/auth.py +93 -42
  5. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/services/firestore.py +10 -2
  6. {empyrebase-2.3.2 → empyrebase-2.3.4/empyrebase.egg-info}/PKG-INFO +19 -3
  7. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase.egg-info/top_level.txt +1 -0
  8. {empyrebase-2.3.2 → empyrebase-2.3.4}/pyproject.toml +1 -1
  9. {empyrebase-2.3.2 → empyrebase-2.3.4}/setup.py +1 -1
  10. {empyrebase-2.3.2 → empyrebase-2.3.4}/LICENSE +0 -0
  11. {empyrebase-2.3.2 → empyrebase-2.3.4}/MANIFEST.in +0 -0
  12. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/__init__.py +0 -0
  13. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/services/__init__.py +0 -0
  14. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/services/database.py +0 -0
  15. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/services/storage.py +0 -0
  16. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/types/__init__.py +0 -0
  17. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/types/closable_sseclient.py +0 -0
  18. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/types/firestore.py +0 -0
  19. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/types/keep_auth_session.py +0 -0
  20. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/types/private.py +0 -0
  21. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/types/pyre.py +0 -0
  22. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/types/pyre_response.py +0 -0
  23. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/types/sseclient.py +0 -0
  24. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/types/stream.py +0 -0
  25. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase/utils/__init__.py +0 -0
  26. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase.egg-info/SOURCES.txt +0 -0
  27. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase.egg-info/dependency_links.txt +0 -0
  28. {empyrebase-2.3.2 → empyrebase-2.3.4}/empyrebase.egg-info/requires.txt +0 -0
  29. {empyrebase-2.3.2 → empyrebase-2.3.4}/setup.cfg +0 -0
  30. {empyrebase-2.3.2 → empyrebase-2.3.4}/tests/test_database.py +0 -0
  31. {empyrebase-2.3.2 → empyrebase-2.3.4}/tests/test_setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: empyrebase
3
- Version: 2.3.2
3
+ Version: 2.3.4
4
4
  Summary: A simple python wrapper for the Firebase API with current deps. Includes Realtime Database, Firestore, Authentication, and Storage.
5
5
  Home-page: https://github.com/emrothenberg/empyrebase
6
6
  Author: emrothenberg
@@ -35,8 +35,24 @@ Dynamic: license-file
35
35
  # Empyrebase
36
36
 
37
37
  A simple python wrapper for the [Firebase API](https://firebase.google.com). Supports Realtime DB, Firestore, Auth and Storage.
38
-
39
- [![PyPI Downloads](https://static.pepy.tech/badge/empyrebase)](https://pepy.tech/projects/empyrebase)
38
+ <p align="center">
39
+ <img src="images/empyrebase-logo.png" alt="Empyrebase logo" width="200"/>
40
+ </p>
41
+
42
+ <p align="center">
43
+ <a href="https://pypi.org/project/empyrebase/">
44
+ <img src="https://img.shields.io/pypi/v/empyrebase.svg" alt="PyPI version">
45
+ </a>
46
+ <a href="https://pepy.tech/project/empyrebase">
47
+ <img src="https://pepy.tech/badge/empyrebase" alt="Downloads">
48
+ </a>
49
+ <a href="https://pypi.org/project/empyrebase/">
50
+ <img src="https://img.shields.io/pypi/pyversions/empyrebase.svg" alt="Python Versions">
51
+ </a>
52
+ <a href="https://github.com/emrothenberg/empyrebase/blob/master/LICENSE">
53
+ <img src="https://img.shields.io/pypi/l/empyrebase.svg" alt="License">
54
+ </a>
55
+ </p>
40
56
 
41
57
  ## Installation
42
58
 
@@ -1,8 +1,24 @@
1
1
  # Empyrebase
2
2
 
3
3
  A simple python wrapper for the [Firebase API](https://firebase.google.com). Supports Realtime DB, Firestore, Auth and Storage.
4
-
5
- [![PyPI Downloads](https://static.pepy.tech/badge/empyrebase)](https://pepy.tech/projects/empyrebase)
4
+ <p align="center">
5
+ <img src="images/empyrebase-logo.png" alt="Empyrebase logo" width="200"/>
6
+ </p>
7
+
8
+ <p align="center">
9
+ <a href="https://pypi.org/project/empyrebase/">
10
+ <img src="https://img.shields.io/pypi/v/empyrebase.svg" alt="PyPI version">
11
+ </a>
12
+ <a href="https://pepy.tech/project/empyrebase">
13
+ <img src="https://pepy.tech/badge/empyrebase" alt="Downloads">
14
+ </a>
15
+ <a href="https://pypi.org/project/empyrebase/">
16
+ <img src="https://img.shields.io/pypi/pyversions/empyrebase.svg" alt="Python Versions">
17
+ </a>
18
+ <a href="https://github.com/emrothenberg/empyrebase/blob/master/LICENSE">
19
+ <img src="https://img.shields.io/pypi/l/empyrebase.svg" alt="License">
20
+ </a>
21
+ </p>
6
22
 
7
23
  ## Installation
8
24
 
@@ -7,7 +7,7 @@ from google.oauth2 import service_account
7
7
  from .services import Auth, Database, Firestore, Storage
8
8
 
9
9
  PACKAGE_NAME = "empyrebase"
10
- CURRENT_VERSION = "2.3.2"
10
+ CURRENT_VERSION = "2.3.4"
11
11
 
12
12
  def warn_if_outdated():
13
13
  try:
@@ -1,10 +1,14 @@
1
+ import socket
2
+ import threading
3
+ from urllib.parse import urlencode, urlparse, parse_qs
4
+ from urllib.parse import unquote # if you still want to unquote()
1
5
  import datetime
2
6
  import json
3
7
  import jwt as jwt
4
8
  from empyrebase.utils import raise_detailed_error
5
9
  import requests
6
10
  from urllib.parse import unquote
7
- from Crypto.PublicKey import RSA
11
+ from http import server
8
12
  import webbrowser
9
13
 
10
14
 
@@ -27,56 +31,103 @@ class Auth:
27
31
  self.current_user = request_object.json()
28
32
  return request_object.json()
29
33
 
30
- def get_google_oauth_token(self, client_id: str, client_secret: str, redirect_uri: str = "http://localhost", scope: str = "email%20profile%20openid") -> str:
34
+ def get_google_oauth_token(self, client_id: str, client_secret: str,
35
+ redirect_uri: str = "http://localhost:80",
36
+ scope: str = "email profile openid") -> str:
31
37
  """
32
- Retrieves an OAuth token from Google using the Client Credentials Flow.
38
+ Opens the user's browser, captures the authorization code automatically when using the
39
+ default localhost redirect, then exchanges it for a token. If a custom redirect_uri is
40
+ provided (non-localhost), falls back to manual paste for the code.
41
+ """
42
+ def build_auth_url(_redirect_uri: str) -> str:
43
+ base = "https://accounts.google.com/o/oauth2/v2/auth"
44
+ q = {
45
+ "response_type": "code",
46
+ "client_id": client_id,
47
+ "redirect_uri": _redirect_uri,
48
+ "scope": scope,
49
+ "access_type": "offline",
50
+ "prompt": "consent",
51
+ }
52
+ return f"{base}?{urlencode(q)}"
33
53
 
34
- This method opens a web browser to redirect the user to the Google authentication page,
35
- and then prompts them to paste the authorization code back in. The resulting access token
36
- is returned as a string.
54
+ code = None
55
+ used_redirect_uri = redirect_uri
37
56
 
38
- Args:
39
- client_id (str): The ID of your application on Google Cloud Console.
40
- client_secret (str): The secret key for your application on Google Cloud Console.
41
- redirect_uri (str, optional): The URL to which the user will be redirected after authorization. Defaults to "http://localhost".
42
- scope (str, optional): A space-separated list of scopes that you want to use with this token. Defaults to "email profile openid".
57
+ if redirect_uri.strip().startswith("http://localhost"):
58
+ port = redirect_uri.strip().lower().replace(
59
+ "http://", "").split(":")[-1]
60
+ if port.startswith("localhost"):
61
+ raise ValueError(
62
+ "Invalid redirect_uri. If using localhost, please specify a port.")
63
+ if not port.isnumeric():
64
+ raise ValueError("Invalid redirect_uri. Port must be numeric.")
43
65
 
44
- Returns:
45
- str: The OAuth access token.
46
- """
47
- base = "https://accounts.google.com/o/oauth2/v2/auth"
48
- auth_url = (
49
- f"{base}?response_type=code"
50
- f"&client_id={client_id}"
51
- f"&redirect_uri={redirect_uri}"
52
- f"&scope={scope}"
53
- f"&access_type=offline"
54
- f"&prompt=consent"
55
- )
56
-
57
- print("Visit this URL to sign in:")
58
- print(auth_url)
59
- webbrowser.open(auth_url)
60
- code = input("Paste the code here: ")
61
- code = unquote(code)
62
-
63
- # Get actual token
66
+ port = int(port)
67
+ sock = socket.socket()
68
+ sock.bind(("127.0.0.1", port))
69
+ sock.close()
70
+
71
+ used_redirect_uri = f"http://localhost{':' + str(port) if port != 80 else ''}"
72
+
73
+ class Handler(server.BaseHTTPRequestHandler):
74
+ def do_GET(self):
75
+ nonlocal code
76
+ qs = parse_qs(urlparse(self.path).query)
77
+ if "code" in qs:
78
+ code = qs["code"][0]
79
+ self.send_response(200)
80
+ self.send_header(
81
+ "Content-Type", "text/html; charset=utf-8")
82
+ self.end_headers()
83
+ self.wfile.write(
84
+ b"<html><body><h1>Authorised</h1>You can close this tab.</body></html>")
85
+ else:
86
+ self.send_response(400)
87
+ self.end_headers()
88
+ # stop server after first request
89
+ threading.Thread(target=httpd.shutdown,
90
+ daemon=True).start()
91
+
92
+ # silence default logging
93
+ def log_message(self, *_): pass
94
+
95
+ httpd = server.HTTPServer(("127.0.0.1", port), Handler)
96
+ t = threading.Thread(target=httpd.serve_forever, daemon=True)
97
+ t.start()
98
+
99
+ # open browser to Google's auth page targeting our local redirect
100
+ webbrowser.open(build_auth_url(used_redirect_uri))
101
+
102
+ # wait for code (with a reasonable timeout)
103
+ t.join(timeout=300) # 5 minutes max wait
104
+
105
+ if code is None:
106
+ raise RuntimeError(
107
+ "No authorization code received on the local redirect.")
108
+ else:
109
+ # CUSTOM redirect (not localhost): let the user paste the code
110
+ auth_url = build_auth_url(used_redirect_uri)
111
+ print("Visit this URL to sign in:")
112
+ print(auth_url)
113
+ webbrowser.open(auth_url)
114
+ code = input("Paste the code here: ").strip()
115
+ code = unquote(code)
116
+
117
+ # Exchange code for token
64
118
  token_url = "https://oauth2.googleapis.com/token"
65
119
  data = {
66
- "code": (code),
120
+ "code": code,
67
121
  "client_id": client_id,
68
122
  "client_secret": client_secret,
69
- "redirect_uri": redirect_uri,
70
- "grant_type": "authorization_code"
123
+ "redirect_uri": used_redirect_uri,
124
+ "grant_type": "authorization_code",
71
125
  }
72
- request_object = requests.post(token_url, data=data)
73
- raise_detailed_error(request_object)
74
- request_json = request_object.json()
75
- # OAuth may use both access_token and id_token, there's no difference.
76
- # Since the postBody parameters use id_token as a param name, I'll use id_token as a first choice
77
- return request_json.get("id_token", request_json.get("access_token"))
78
- # Since the postBody parameters use id_token as a param name, I'll use id_token as a first choice
79
- return request_json.get("id_token", request_json.get("access_token"))
126
+ r = requests.post(token_url, data=data)
127
+ raise_detailed_error(r)
128
+ j = r.json()
129
+ token = j.get("id_token", j.get("access_token"))
130
+ return token
80
131
 
81
132
  def sign_in_with_oauth(self, oauth_token: str, provider_id: str = "google.com", request_uri: str = "http://localhost") -> dict:
82
133
  """
@@ -174,9 +174,17 @@ class Firestore:
174
174
 
175
175
  if not document and not documents:
176
176
  raise ValueError("Document path is required")
177
-
177
+
178
178
  if document:
179
- documents.append(document)
179
+ path_segments = document.strip("/").split("/")
180
+ if len(collections) == len(documents):
181
+ collections.extend(path_segments[0::2])
182
+ if len(path_segments) > 1:
183
+ documents.extend(path_segments[1::2])
184
+ else:
185
+ documents.extend(path_segments[0::2])
186
+ if len(path_segments) > 1:
187
+ collections.extend(path_segments[1::2])
180
188
 
181
189
  if len(collections) != len(documents):
182
190
  raise ValueError("Document ref must be an even child.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: empyrebase
3
- Version: 2.3.2
3
+ Version: 2.3.4
4
4
  Summary: A simple python wrapper for the Firebase API with current deps. Includes Realtime Database, Firestore, Authentication, and Storage.
5
5
  Home-page: https://github.com/emrothenberg/empyrebase
6
6
  Author: emrothenberg
@@ -35,8 +35,24 @@ Dynamic: license-file
35
35
  # Empyrebase
36
36
 
37
37
  A simple python wrapper for the [Firebase API](https://firebase.google.com). Supports Realtime DB, Firestore, Auth and Storage.
38
-
39
- [![PyPI Downloads](https://static.pepy.tech/badge/empyrebase)](https://pepy.tech/projects/empyrebase)
38
+ <p align="center">
39
+ <img src="images/empyrebase-logo.png" alt="Empyrebase logo" width="200"/>
40
+ </p>
41
+
42
+ <p align="center">
43
+ <a href="https://pypi.org/project/empyrebase/">
44
+ <img src="https://img.shields.io/pypi/v/empyrebase.svg" alt="PyPI version">
45
+ </a>
46
+ <a href="https://pepy.tech/project/empyrebase">
47
+ <img src="https://pepy.tech/badge/empyrebase" alt="Downloads">
48
+ </a>
49
+ <a href="https://pypi.org/project/empyrebase/">
50
+ <img src="https://img.shields.io/pypi/pyversions/empyrebase.svg" alt="Python Versions">
51
+ </a>
52
+ <a href="https://github.com/emrothenberg/empyrebase/blob/master/LICENSE">
53
+ <img src="https://img.shields.io/pypi/l/empyrebase.svg" alt="License">
54
+ </a>
55
+ </p>
40
56
 
41
57
  ## Installation
42
58
 
@@ -1,3 +1,4 @@
1
1
  dist
2
2
  empyrebase
3
+ images
3
4
  venv
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "empyrebase"
7
- version = "2.3.2"
7
+ version = "2.3.4"
8
8
  description = "A simple python wrapper for the Firebase API with current deps. Includes Realtime Database, Firestore, Authentication, and Storage."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -5,7 +5,7 @@ with open('README.md') as f:
5
5
 
6
6
  setup(
7
7
  name='empyrebase',
8
- version='2.3.2',
8
+ version='2.3.4',
9
9
  url='https://github.com/emrothenberg/empyrebase',
10
10
  description="A simple python wrapper for the Firebase API with current deps. Includes Realtime Database, Firestore, Authentication, and Storage.",
11
11
  long_description=long_description,
File without changes
File without changes
File without changes