nextpy-framework 1.1.0__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 (55) hide show
  1. nextpy_framework-1.1.0/.nextpy_framework/nextpy/__init__.py +52 -0
  2. nextpy_framework-1.1.0/.nextpy_framework/nextpy/auth.py +94 -0
  3. nextpy_framework-1.1.0/.nextpy_framework/nextpy/builder.py +123 -0
  4. nextpy_framework-1.1.0/.nextpy_framework/nextpy/cli.py +595 -0
  5. nextpy_framework-1.1.0/.nextpy_framework/nextpy/components/__init__.py +45 -0
  6. nextpy_framework-1.1.0/.nextpy_framework/nextpy/components/feedback.py +210 -0
  7. nextpy_framework-1.1.0/.nextpy_framework/nextpy/components/form.py +346 -0
  8. nextpy_framework-1.1.0/.nextpy_framework/nextpy/components/head.py +167 -0
  9. nextpy_framework-1.1.0/.nextpy_framework/nextpy/components/hooks_provider.py +64 -0
  10. nextpy_framework-1.1.0/.nextpy_framework/nextpy/components/image.py +180 -0
  11. nextpy_framework-1.1.0/.nextpy_framework/nextpy/components/layout.py +206 -0
  12. nextpy_framework-1.1.0/.nextpy_framework/nextpy/components/link.py +132 -0
  13. nextpy_framework-1.1.0/.nextpy_framework/nextpy/components/loader.py +65 -0
  14. nextpy_framework-1.1.0/.nextpy_framework/nextpy/components/toast.py +101 -0
  15. nextpy_framework-1.1.0/.nextpy_framework/nextpy/components/visual.py +185 -0
  16. nextpy_framework-1.1.0/.nextpy_framework/nextpy/config.py +75 -0
  17. nextpy_framework-1.1.0/.nextpy_framework/nextpy/core/__init__.py +21 -0
  18. nextpy_framework-1.1.0/.nextpy_framework/nextpy/core/builder.py +237 -0
  19. nextpy_framework-1.1.0/.nextpy_framework/nextpy/core/data_fetching.py +221 -0
  20. nextpy_framework-1.1.0/.nextpy_framework/nextpy/core/renderer.py +252 -0
  21. nextpy_framework-1.1.0/.nextpy_framework/nextpy/core/router.py +233 -0
  22. nextpy_framework-1.1.0/.nextpy_framework/nextpy/core/sync.py +34 -0
  23. nextpy_framework-1.1.0/.nextpy_framework/nextpy/db.py +121 -0
  24. nextpy_framework-1.1.0/.nextpy_framework/nextpy/dev_server.py +69 -0
  25. nextpy_framework-1.1.0/.nextpy_framework/nextpy/dev_tools.py +157 -0
  26. nextpy_framework-1.1.0/.nextpy_framework/nextpy/errors.py +70 -0
  27. nextpy_framework-1.1.0/.nextpy_framework/nextpy/hooks.py +348 -0
  28. nextpy_framework-1.1.0/.nextpy_framework/nextpy/performance.py +78 -0
  29. nextpy_framework-1.1.0/.nextpy_framework/nextpy/plugins.py +61 -0
  30. nextpy_framework-1.1.0/.nextpy_framework/nextpy/py.typed +0 -0
  31. nextpy_framework-1.1.0/.nextpy_framework/nextpy/server/__init__.py +6 -0
  32. nextpy_framework-1.1.0/.nextpy_framework/nextpy/server/app.py +325 -0
  33. nextpy_framework-1.1.0/.nextpy_framework/nextpy/server/debug.py +93 -0
  34. nextpy_framework-1.1.0/.nextpy_framework/nextpy/server/middleware.py +88 -0
  35. nextpy_framework-1.1.0/.nextpy_framework/nextpy/utils/__init__.py +0 -0
  36. nextpy_framework-1.1.0/.nextpy_framework/nextpy/utils/cache.py +89 -0
  37. nextpy_framework-1.1.0/.nextpy_framework/nextpy/utils/email.py +59 -0
  38. nextpy_framework-1.1.0/.nextpy_framework/nextpy/utils/file_upload.py +65 -0
  39. nextpy_framework-1.1.0/.nextpy_framework/nextpy/utils/logging.py +52 -0
  40. nextpy_framework-1.1.0/.nextpy_framework/nextpy/utils/search.py +59 -0
  41. nextpy_framework-1.1.0/.nextpy_framework/nextpy/utils/seo.py +85 -0
  42. nextpy_framework-1.1.0/.nextpy_framework/nextpy/utils/validators.py +58 -0
  43. nextpy_framework-1.1.0/.nextpy_framework/nextpy/websocket.py +76 -0
  44. nextpy_framework-1.1.0/.nextpy_framework/nextpy_framework.egg-info/PKG-INFO +343 -0
  45. nextpy_framework-1.1.0/.nextpy_framework/nextpy_framework.egg-info/SOURCES.txt +53 -0
  46. nextpy_framework-1.1.0/.nextpy_framework/nextpy_framework.egg-info/dependency_links.txt +1 -0
  47. nextpy_framework-1.1.0/.nextpy_framework/nextpy_framework.egg-info/entry_points.txt +2 -0
  48. nextpy_framework-1.1.0/.nextpy_framework/nextpy_framework.egg-info/requires.txt +16 -0
  49. nextpy_framework-1.1.0/.nextpy_framework/nextpy_framework.egg-info/top_level.txt +1 -0
  50. nextpy_framework-1.1.0/LICENSE +21 -0
  51. nextpy_framework-1.1.0/PKG-INFO +343 -0
  52. nextpy_framework-1.1.0/README.md +304 -0
  53. nextpy_framework-1.1.0/pyproject.toml +70 -0
  54. nextpy_framework-1.1.0/setup.cfg +4 -0
  55. nextpy_framework-1.1.0/tests/test_routing.py +50 -0
@@ -0,0 +1,52 @@
1
+ """
2
+ NextPy - A Python web framework inspired by Next.js
3
+ File-based routing, SSR, SSG, and more with FastAPI + Jinja2
4
+ """
5
+
6
+ __version__ = "1.0.1"
7
+
8
+ from nextpy.core.router import Router, Route, DynamicRoute
9
+ from nextpy.core.renderer import Renderer
10
+ from nextpy.core.data_fetching import (
11
+ get_server_side_props,
12
+ get_static_props,
13
+ get_static_paths,
14
+ )
15
+ from nextpy.components.head import Head
16
+ from nextpy.components.link import Link
17
+ from nextpy.server.app import create_app
18
+ from nextpy.hooks import (
19
+ useState,
20
+ useEffect,
21
+ useContext,
22
+ useReducer,
23
+ useCallback,
24
+ useMemo,
25
+ useRef,
26
+ useGlobalState,
27
+ component,
28
+ )
29
+
30
+ __all__ = [
31
+ "Router",
32
+ "Route",
33
+ "DynamicRoute",
34
+ "Renderer",
35
+ "get_server_side_props",
36
+ "get_static_props",
37
+ "get_static_paths",
38
+ "Head",
39
+ "Link",
40
+ "create_app",
41
+ "useState",
42
+ "useEffect",
43
+ "useContext",
44
+ "useReducer",
45
+ "useCallback",
46
+ "useMemo",
47
+ "useRef",
48
+ "useGlobalState",
49
+ "component",
50
+ "maintainers",
51
+ "main"
52
+ ]
@@ -0,0 +1,94 @@
1
+ """
2
+ NextPy Authentication - JWT and Session support
3
+ """
4
+
5
+ import jwt
6
+ from datetime import datetime, timedelta
7
+ from typing import Optional, Dict, Any
8
+ from functools import wraps
9
+ from fastapi import HTTPException, Request
10
+ from nextpy.config import settings
11
+
12
+
13
+ class AuthManager:
14
+ """Handle JWT and session authentication"""
15
+
16
+ @staticmethod
17
+ def create_token(user_id: int, expires_delta: Optional[timedelta] = None) -> str:
18
+ """Create JWT token"""
19
+ if expires_delta is None:
20
+ expires_delta = timedelta(hours=settings.jwt_expiration_hours)
21
+
22
+ expire = datetime.utcnow() + expires_delta
23
+ payload = {"user_id": user_id, "exp": expire}
24
+
25
+ return jwt.encode(payload, settings.jwt_secret, algorithm=settings.jwt_algorithm)
26
+
27
+ @staticmethod
28
+ def verify_token(token: str) -> Optional[int]:
29
+ """Verify JWT token and return user_id"""
30
+ try:
31
+ payload = jwt.decode(token, settings.jwt_secret, algorithms=[settings.jwt_algorithm])
32
+ user_id = payload.get("user_id")
33
+ if user_id is None:
34
+ return None
35
+ return user_id
36
+ except jwt.ExpiredSignatureError:
37
+ raise HTTPException(status_code=401, detail="Token expired")
38
+ except jwt.InvalidTokenError:
39
+ raise HTTPException(status_code=401, detail="Invalid token")
40
+
41
+ @staticmethod
42
+ def get_token_from_request(request: Request) -> Optional[str]:
43
+ """Extract token from Authorization header"""
44
+ auth_header = request.headers.get("Authorization", "")
45
+ if auth_header.startswith("Bearer "):
46
+ return auth_header[7:]
47
+ return None
48
+
49
+
50
+ def require_auth(func):
51
+ """Decorator to require authentication"""
52
+ @wraps(func)
53
+ async def wrapper(request: Request, *args, **kwargs):
54
+ token = AuthManager.get_token_from_request(request)
55
+ if not token:
56
+ raise HTTPException(status_code=401, detail="Missing token")
57
+
58
+ user_id = AuthManager.verify_token(token)
59
+ request.state.user_id = user_id
60
+ return await func(request, *args, **kwargs)
61
+
62
+ return wrapper
63
+
64
+
65
+ # Session storage (in-memory, use Redis for production)
66
+ _sessions: Dict[str, Dict[str, Any]] = {}
67
+
68
+
69
+ def create_session(user_id: int) -> str:
70
+ """Create session"""
71
+ import uuid
72
+ session_id = str(uuid.uuid4())
73
+ _sessions[session_id] = {
74
+ "user_id": user_id,
75
+ "created_at": datetime.utcnow(),
76
+ "expires_at": datetime.utcnow() + timedelta(hours=24)
77
+ }
78
+ return session_id
79
+
80
+
81
+ def get_session(session_id: str) -> Optional[Dict[str, Any]]:
82
+ """Get session"""
83
+ session = _sessions.get(session_id)
84
+ if session and session["expires_at"] > datetime.utcnow():
85
+ return session
86
+ return None
87
+
88
+
89
+ def delete_session(session_id: str) -> bool:
90
+ """Delete session"""
91
+ if session_id in _sessions:
92
+ del _sessions[session_id]
93
+ return True
94
+ return False
@@ -0,0 +1,123 @@
1
+ """
2
+ NextPy Build Optimizer - Fast builds with caching and parallel processing
3
+ """
4
+
5
+ import asyncio
6
+ from pathlib import Path
7
+ from typing import List, Dict, Any, Optional
8
+ import hashlib
9
+ import json
10
+ from datetime import datetime
11
+
12
+
13
+ class BuildCache:
14
+ """Smart build caching to skip unchanged files"""
15
+
16
+ def __init__(self, cache_dir: str = ".nextpy_cache"):
17
+ self.cache_dir = Path(cache_dir)
18
+ self.cache_dir.mkdir(exist_ok=True)
19
+ self.cache_file = self.cache_dir / "build_cache.json"
20
+ self.cache: Dict[str, str] = self._load_cache()
21
+
22
+ def _load_cache(self) -> Dict[str, str]:
23
+ if self.cache_file.exists():
24
+ with open(self.cache_file) as f:
25
+ return json.load(f)
26
+ return {}
27
+
28
+ def _save_cache(self) -> None:
29
+ with open(self.cache_file, "w") as f:
30
+ json.dump(self.cache, f)
31
+
32
+ def get_hash(self, file_path: str) -> str:
33
+ """Get SHA256 hash of file"""
34
+ with open(file_path, "rb") as f:
35
+ return hashlib.sha256(f.read()).hexdigest()
36
+
37
+ def is_changed(self, file_path: str) -> bool:
38
+ """Check if file has changed since last build"""
39
+ current_hash = self.get_hash(file_path)
40
+ cached_hash = self.cache.get(file_path)
41
+
42
+ if cached_hash == current_hash:
43
+ return False
44
+
45
+ self.cache[file_path] = current_hash
46
+ self._save_cache()
47
+ return True
48
+
49
+ def clear(self) -> None:
50
+ """Clear cache"""
51
+ self.cache = {}
52
+ if self.cache_file.exists():
53
+ self.cache_file.unlink()
54
+
55
+
56
+ class ParallelBuilder:
57
+ """Build with parallel processing for faster builds"""
58
+
59
+ @staticmethod
60
+ async def build_pages(pages: List[Path], concurrency: int = 4) -> List[Dict[str, Any]]:
61
+ """Build multiple pages in parallel"""
62
+ results: List[Dict[str, Any]] = []
63
+ semaphore = asyncio.Semaphore(concurrency)
64
+
65
+ async def build_page(page: Path) -> Dict[str, Any]:
66
+ async with semaphore:
67
+ return await ParallelBuilder._build_single_page(page)
68
+
69
+ tasks = [build_page(page) for page in pages]
70
+ results = await asyncio.gather(*tasks)
71
+ return results
72
+
73
+ @staticmethod
74
+ async def _build_single_page(page: Path) -> Dict[str, Any]:
75
+ """Build a single page"""
76
+ await asyncio.sleep(0.1) # Simulate build work
77
+ return {
78
+ "page": str(page),
79
+ "status": "built",
80
+ "timestamp": datetime.now().isoformat()
81
+ }
82
+
83
+
84
+ class BuildOptimizer:
85
+ """Optimize bundle size and build time"""
86
+
87
+ @staticmethod
88
+ def analyze_bundle(output_dir: Path) -> Dict[str, Any]:
89
+ """Analyze built bundle size"""
90
+ total_size: int = 0
91
+ files: Dict[str, int] = {}
92
+
93
+ for file in output_dir.rglob("*"):
94
+ if file.is_file():
95
+ size = file.stat().st_size
96
+ total_size += size
97
+ files[str(file.relative_to(output_dir))] = size
98
+
99
+ # Sort by size
100
+ sorted_files = sorted(files.items(), key=lambda x: x[1], reverse=True)
101
+
102
+ return {
103
+ "total_size": total_size,
104
+ "total_size_mb": round(total_size / 1024 / 1024, 2),
105
+ "files": dict(sorted_files[:10]), # Top 10 largest
106
+ "file_count": len(files)
107
+ }
108
+
109
+ @staticmethod
110
+ def compress_assets(output_dir: Path) -> int:
111
+ """Compress CSS and JS files"""
112
+ import gzip
113
+ compressed_count: int = 0
114
+
115
+ for file in list(output_dir.rglob("*.js")) + list(output_dir.rglob("*.css")):
116
+ if file.is_file():
117
+ with open(file, "rb") as f_in:
118
+ content = f_in.read()
119
+ with gzip.open(f"{file}.gz", "wb") as f_out:
120
+ f_out.write(content)
121
+ compressed_count += 1
122
+
123
+ return compressed_count