Compare commits
28 Commits
version-1.
...
master
Author | SHA1 | Date | |
---|---|---|---|
d940735453 | |||
7d71067b3d | |||
b0b804410d | |||
d55f2dcee1 | |||
2fbb5ac788 | |||
b47b407589 | |||
a136366804 | |||
6718e1646f | |||
9d7f9a979e | |||
326a793de0 | |||
88ccb0f376 | |||
5b8895278a | |||
eb37d20039 | |||
4f49e8ca0b | |||
c931700fec | |||
6285826bfc | |||
25150397c0 | |||
c67b161e3d | |||
f76dcb964e | |||
2d868ec724 | |||
666ae0fa90 | |||
49844e17b2 | |||
06432a3534 | |||
3a2e9bc672 | |||
ce331c7b22 | |||
8b5dc65d84 | |||
3bc7dcfa9e | |||
d9fe24ece7 |
39
CHANGELOG.md
39
CHANGELOG.md
@ -1,6 +1,43 @@
|
|||||||
# Blender Cloud changelog
|
# Blender Cloud changelog
|
||||||
|
|
||||||
## Version 1.18 (in development)
|
|
||||||
|
## Version 1.25 (2022-02-25)
|
||||||
|
|
||||||
|
- Compatibility with Blender 3.1 (Python 3.10).
|
||||||
|
- Bump blender-asset-tracer to version 1.11, for UDIM support.
|
||||||
|
|
||||||
|
|
||||||
|
## Version 1.24 (2022-02-04)
|
||||||
|
|
||||||
|
- Bump blender-asset-tracer version 1.8 → 1.10, for fixing a bug where files were doubly-compressed.
|
||||||
|
|
||||||
|
|
||||||
|
## Version 1.23 (2021-11-09)
|
||||||
|
|
||||||
|
- Bump blender-asset-tracer version 1.7 → 1.8, for compatibility with sending read-only blend files to Flamenco.
|
||||||
|
|
||||||
|
|
||||||
|
## Version 1.22 (2021-11-05)
|
||||||
|
|
||||||
|
- Fix Windows incompatibility when using Shaman URLs as job storage path.
|
||||||
|
- Bump blender-asset-tracer version 1.6 → 1.7, for compatibility with files compressed by Blender 3.0.
|
||||||
|
|
||||||
|
|
||||||
|
## Version 1.21 (2021-07-27)
|
||||||
|
|
||||||
|
- Bump blender-asset-tracer version 1.5.1 → 1.6, for better compatibility with Geometry Nodes.
|
||||||
|
|
||||||
|
## Version 1.20 (2021-07-22)
|
||||||
|
|
||||||
|
- Bump blender-asset-tracer version 1.3.1 -> 1.5.1.
|
||||||
|
- Blender-asset-tracer "Strict Pointer Mode" disabled, to avoid issues with
|
||||||
|
not-entirely-synced library overrides.
|
||||||
|
|
||||||
|
## Version 1.19 (2021-02-23)
|
||||||
|
|
||||||
|
- Another Python 3.9+ compatibility fix.
|
||||||
|
|
||||||
|
## Version 1.18 (2021-02-16)
|
||||||
|
|
||||||
- Add compatibility with Python 3.9 (as used in Blender 2.93).
|
- Add compatibility with Python 3.9 (as used in Blender 2.93).
|
||||||
- Drop compatibility with Blender 2.79 and older. The last version of the
|
- Drop compatibility with Blender 2.79 and older. The last version of the
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "Blender Cloud",
|
"name": "Blender Cloud",
|
||||||
"author": "Sybren A. Stüvel, Francesco Siddi, Inês Almeida, Antony Riakiotakis",
|
"author": "Sybren A. Stüvel, Francesco Siddi, Inês Almeida, Antony Riakiotakis",
|
||||||
"version": (1, 17),
|
"version": (1, 25),
|
||||||
"blender": (2, 80, 0),
|
"blender": (2, 80, 0),
|
||||||
"location": "Addon Preferences panel, and Ctrl+Shift+Alt+A anywhere for texture browser",
|
"location": "Addon Preferences panel, and Ctrl+Shift+Alt+A anywhere for texture browser",
|
||||||
"description": "Texture library browser and Blender Sync. Requires the Blender ID addon "
|
"description": "Texture library browser and Blender Sync. Requires the Blender ID addon "
|
||||||
|
@ -56,8 +56,11 @@ def setup_asyncio_executor():
|
|||||||
|
|
||||||
from . import pillar
|
from . import pillar
|
||||||
|
|
||||||
|
# Python 3.8 deprecated the 'loop' parameter, 3.10 removed it.
|
||||||
|
kwargs = {"loop": loop} if sys.version_info < (3, 8) else {}
|
||||||
|
|
||||||
# No more than this many Pillar calls should be made simultaneously
|
# No more than this many Pillar calls should be made simultaneously
|
||||||
pillar.pillar_semaphore = asyncio.Semaphore(3, loop=loop)
|
pillar.pillar_semaphore = asyncio.Semaphore(3, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def kick_async_loop(*args) -> bool:
|
def kick_async_loop(*args) -> bool:
|
||||||
|
@ -668,6 +668,8 @@ class ATTRACT_OT_open_meta_blendfile(AttractOperatorMixin, Operator):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
|
if context.selected_sequences is None:
|
||||||
|
return False
|
||||||
return bool(
|
return bool(
|
||||||
any(cls.filename_from_metadata(s) for s in context.selected_sequences)
|
any(cls.filename_from_metadata(s) for s in context.selected_sequences)
|
||||||
)
|
)
|
||||||
|
@ -47,7 +47,7 @@ log = logging.getLogger(__name__)
|
|||||||
icons = None
|
icons = None
|
||||||
|
|
||||||
|
|
||||||
@pyside_cache("version")
|
@pyside_cache
|
||||||
def blender_syncable_versions(self, context):
|
def blender_syncable_versions(self, context):
|
||||||
"""Returns the list of items used by SyncStatusProperties.version EnumProperty."""
|
"""Returns the list of items used by SyncStatusProperties.version EnumProperty."""
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ class SyncStatusProperties(PropertyGroup):
|
|||||||
self["available_blender_versions"] = new_versions
|
self["available_blender_versions"] = new_versions
|
||||||
|
|
||||||
|
|
||||||
@pyside_cache("project")
|
@pyside_cache
|
||||||
def bcloud_available_projects(self, context):
|
def bcloud_available_projects(self, context):
|
||||||
"""Returns the list of items used by BlenderCloudProjectGroup.project EnumProperty."""
|
"""Returns the list of items used by BlenderCloudProjectGroup.project EnumProperty."""
|
||||||
|
|
||||||
|
@ -42,11 +42,10 @@ else:
|
|||||||
from .. import blender
|
from .. import blender
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from bpy.types import AddonPreferences, Operator, WindowManager, Scene, PropertyGroup
|
from bpy.types import Operator, PropertyGroup
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
StringProperty,
|
StringProperty,
|
||||||
EnumProperty,
|
EnumProperty,
|
||||||
PointerProperty,
|
|
||||||
BoolProperty,
|
BoolProperty,
|
||||||
IntProperty,
|
IntProperty,
|
||||||
)
|
)
|
||||||
@ -54,6 +53,8 @@ from bpy.props import (
|
|||||||
from .. import async_loop, pillar, project_specific, utils
|
from .. import async_loop, pillar, project_specific, utils
|
||||||
from ..utils import pyside_cache, redraw
|
from ..utils import pyside_cache, redraw
|
||||||
|
|
||||||
|
import blender_asset_tracer.blendfile
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Global flag used to determine whether panels etc. can be drawn.
|
# Global flag used to determine whether panels etc. can be drawn.
|
||||||
@ -91,7 +92,7 @@ def scene_sample_count(scene) -> int:
|
|||||||
return samples
|
return samples
|
||||||
|
|
||||||
|
|
||||||
@pyside_cache("manager")
|
@pyside_cache
|
||||||
def available_managers(self, context):
|
def available_managers(self, context):
|
||||||
"""Returns the list of items used by a manager-selector EnumProperty."""
|
"""Returns the list of items used by a manager-selector EnumProperty."""
|
||||||
|
|
||||||
@ -269,7 +270,7 @@ def is_file_inside_job_storage(prefs, current_file: typing.Union[str, Path]) ->
|
|||||||
|
|
||||||
if isinstance(current_file, str):
|
if isinstance(current_file, str):
|
||||||
# Shaman URLs are always remote, so the current file cannot be in there.
|
# Shaman URLs are always remote, so the current file cannot be in there.
|
||||||
if is_shaman_url(current_file):
|
if is_shaman_url(prefs.flamenco_job_file_path):
|
||||||
return False
|
return False
|
||||||
current_file = Path(current_file)
|
current_file = Path(current_file)
|
||||||
|
|
||||||
@ -649,6 +650,12 @@ class FLAMENCO_OT_render(
|
|||||||
|
|
||||||
self.log.debug("projdir: %s", projdir)
|
self.log.debug("projdir: %s", projdir)
|
||||||
|
|
||||||
|
# Due to issues with library overrides and unsynced pointers, it's quite
|
||||||
|
# common for the Blender Animation Studio to get crashes of BAT. To avoid
|
||||||
|
# these, Strict Pointer Mode is disabled.
|
||||||
|
|
||||||
|
blender_asset_tracer.blendfile.set_strict_pointer_mode(False)
|
||||||
|
|
||||||
if is_shaman_url(prefs.flamenco_job_file_path):
|
if is_shaman_url(prefs.flamenco_job_file_path):
|
||||||
endpoint, _ = bat_interface.parse_shaman_endpoint(
|
endpoint, _ = bat_interface.parse_shaman_endpoint(
|
||||||
prefs.flamenco_job_file_path
|
prefs.flamenco_job_file_path
|
||||||
@ -947,7 +954,7 @@ async def create_job(
|
|||||||
*,
|
*,
|
||||||
priority: int = 50,
|
priority: int = 50,
|
||||||
job_description: str = None,
|
job_description: str = None,
|
||||||
start_paused=False
|
start_paused=False,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Creates a render job at Flamenco Server, returning the job object as dictionary."""
|
"""Creates a render job at Flamenco Server, returning the job object as dictionary."""
|
||||||
|
|
||||||
@ -1000,7 +1007,7 @@ def _render_output_path(
|
|||||||
render_image_format: str,
|
render_image_format: str,
|
||||||
flamenco_render_frame_range: str,
|
flamenco_render_frame_range: str,
|
||||||
*,
|
*,
|
||||||
include_rel_path: bool = True
|
include_rel_path: bool = True,
|
||||||
) -> typing.Optional[PurePath]:
|
) -> typing.Optional[PurePath]:
|
||||||
"""Cached version of render_output_path()
|
"""Cached version of render_output_path()
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ import logging
|
|||||||
from contextlib import closing, contextmanager
|
from contextlib import closing, contextmanager
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import sys
|
||||||
|
|
||||||
import requests.adapters
|
import requests.adapters
|
||||||
import requests.packages.urllib3.util.retry
|
import requests.packages.urllib3.util.retry
|
||||||
@ -261,14 +262,17 @@ async def pillar_call(pillar_func, *args, caching=True, **kwargs):
|
|||||||
)
|
)
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
|
# Python 3.8 deprecated the 'loop' parameter, 3.10 removed it.
|
||||||
|
kwargs = {"loop": loop} if sys.version_info < (3, 8) else {}
|
||||||
|
|
||||||
# Use explicit calls to acquire() and release() so that we have more control over
|
# Use explicit calls to acquire() and release() so that we have more control over
|
||||||
# how long we wait and how we handle timeouts.
|
# how long we wait and how we handle timeouts.
|
||||||
try:
|
try:
|
||||||
await asyncio.wait_for(pillar_semaphore.acquire(), timeout=10, loop=loop)
|
await asyncio.wait_for(pillar_semaphore.acquire(), timeout=10, **kwargs)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
log.info("Waiting for semaphore to call %s", pillar_func.__name__)
|
log.info("Waiting for semaphore to call %s", pillar_func.__name__)
|
||||||
try:
|
try:
|
||||||
await asyncio.wait_for(pillar_semaphore.acquire(), timeout=50, loop=loop)
|
await asyncio.wait_for(pillar_semaphore.acquire(), timeout=50, **kwargs)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
raise RuntimeError("Timeout waiting for Pillar Semaphore!")
|
raise RuntimeError("Timeout waiting for Pillar Semaphore!")
|
||||||
|
|
||||||
@ -648,9 +652,11 @@ async def fetch_texture_thumbs(
|
|||||||
for texture_node in texture_nodes
|
for texture_node in texture_nodes
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Python 3.8 deprecated the 'loop' parameter, 3.10 removed it.
|
||||||
|
kwargs = {"loop": asyncio.get_event_loop()} if sys.version_info < (3, 8) else {}
|
||||||
|
|
||||||
# raises any exception from failed handle_texture_node() calls.
|
# raises any exception from failed handle_texture_node() calls.
|
||||||
loop = asyncio.get_event_loop()
|
await asyncio.gather(*coros, **kwargs)
|
||||||
await asyncio.gather(*coros, loop=loop)
|
|
||||||
|
|
||||||
log.info("fetch_texture_thumbs: Done downloading texture thumbnails")
|
log.info("fetch_texture_thumbs: Done downloading texture thumbnails")
|
||||||
|
|
||||||
@ -929,8 +935,10 @@ async def download_texture(
|
|||||||
)
|
)
|
||||||
downloaders.append(dlr)
|
downloaders.append(dlr)
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
# Python 3.8 deprecated the 'loop' parameter, 3.10 removed it.
|
||||||
return await asyncio.gather(*downloaders, return_exceptions=True, loop=loop)
|
kwargs = {"loop": asyncio.get_event_loop()} if sys.version_info < (3, 8) else {}
|
||||||
|
|
||||||
|
return await asyncio.gather(*downloaders, return_exceptions=True, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
async def upload_file(
|
async def upload_file(
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
import json
|
import json
|
||||||
import pathlib
|
import pathlib
|
||||||
import typing
|
import typing
|
||||||
|
from typing import Any, Dict, Optional, Tuple
|
||||||
|
|
||||||
|
|
||||||
def sizeof_fmt(num: int, suffix="B") -> str:
|
def sizeof_fmt(num: int, suffix="B") -> str:
|
||||||
@ -35,7 +36,7 @@ def sizeof_fmt(num: int, suffix="B") -> str:
|
|||||||
return "%.1f Yi%s" % (num, suffix)
|
return "%.1f Yi%s" % (num, suffix)
|
||||||
|
|
||||||
|
|
||||||
def find_in_path(path: pathlib.Path, filename: str) -> typing.Optional[pathlib.Path]:
|
def find_in_path(path: pathlib.Path, filename: str) -> Optional[pathlib.Path]:
|
||||||
"""Performs a breadth-first search for the filename.
|
"""Performs a breadth-first search for the filename.
|
||||||
|
|
||||||
Returns the path that contains the file, or None if not found.
|
Returns the path that contains the file, or None if not found.
|
||||||
@ -66,41 +67,29 @@ def find_in_path(path: pathlib.Path, filename: str) -> typing.Optional[pathlib.P
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def pyside_cache(propname):
|
# Mapping from (module name, function name) to the last value returned by that function.
|
||||||
|
_pyside_cache: Dict[Tuple[str, str], Any] = {}
|
||||||
|
|
||||||
|
|
||||||
|
def pyside_cache(wrapped):
|
||||||
"""Decorator, stores the result of the decorated callable in Python-managed memory.
|
"""Decorator, stores the result of the decorated callable in Python-managed memory.
|
||||||
|
|
||||||
This is to work around the warning at
|
This is to work around the warning at
|
||||||
https://www.blender.org/api/blender_python_api_master/bpy.props.html#bpy.props.EnumProperty
|
https://www.blender.org/api/blender_python_api_master/bpy.props.html#bpy.props.EnumProperty
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if callable(propname):
|
import functools
|
||||||
raise TypeError('Usage: pyside_cache("property_name")')
|
|
||||||
|
|
||||||
def decorator(wrapped):
|
@functools.wraps(wrapped)
|
||||||
"""Stores the result of the callable in Python-managed memory.
|
# We can't use (*args, **kwargs), because EnumProperty explicitly checks
|
||||||
|
# for the number of fixed positional arguments.
|
||||||
This is to work around the warning at
|
def decorator(self, context):
|
||||||
https://www.blender.org/api/blender_python_api_master/bpy.props.html#bpy.props.EnumProperty
|
result = None
|
||||||
"""
|
try:
|
||||||
|
result = wrapped(self, context)
|
||||||
import functools
|
return result
|
||||||
|
finally:
|
||||||
@functools.wraps(wrapped)
|
_pyside_cache[wrapped.__module__, wrapped.__name__] = result
|
||||||
# We can't use (*args, **kwargs), because EnumProperty explicitly checks
|
|
||||||
# for the number of fixed positional arguments.
|
|
||||||
def wrapper(self, context):
|
|
||||||
result = None
|
|
||||||
try:
|
|
||||||
result = wrapped(self, context)
|
|
||||||
return result
|
|
||||||
finally:
|
|
||||||
try:
|
|
||||||
rna_type, rna_info = self.bl_rna.__annotations__[propname]
|
|
||||||
except AttributeError:
|
|
||||||
rna_type, rna_info = getattr(self.bl_rna, propname)
|
|
||||||
rna_info["_cached_result"] = result
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
@ -58,8 +58,12 @@ def wheel_filename(fname_prefix: str) -> str:
|
|||||||
if not wheels:
|
if not wheels:
|
||||||
raise RuntimeError("Unable to find wheel at %r" % path_pattern)
|
raise RuntimeError("Unable to find wheel at %r" % path_pattern)
|
||||||
|
|
||||||
# If there are multiple wheels that match, load the latest one.
|
# If there are multiple wheels that match, load the last-modified one.
|
||||||
wheels.sort()
|
# Alphabetical sorting isn't going to cut it since BAT 1.10 was released.
|
||||||
|
def modtime(filename: str) -> int:
|
||||||
|
return os.stat(filename).st_mtime
|
||||||
|
|
||||||
|
wheels.sort(key=modtime)
|
||||||
return wheels[-1]
|
return wheels[-1]
|
||||||
|
|
||||||
|
|
||||||
@ -68,3 +72,8 @@ def load_wheels():
|
|||||||
load_wheel("lockfile", "lockfile")
|
load_wheel("lockfile", "lockfile")
|
||||||
load_wheel("cachecontrol", "CacheControl")
|
load_wheel("cachecontrol", "CacheControl")
|
||||||
load_wheel("pillarsdk", "pillarsdk")
|
load_wheel("pillarsdk", "pillarsdk")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
wheel = wheel_filename("blender_asset_tracer")
|
||||||
|
print(f"Wheel: {wheel}")
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
lockfile==0.12.2
|
lockfile==0.12.2
|
||||||
pillarsdk==1.8.0
|
pillarsdk==1.8.0
|
||||||
wheel==0.29.0
|
wheel==0.29.0
|
||||||
blender-asset-tracer==1.3.1
|
blender-asset-tracer==1.11
|
||||||
|
|
||||||
# Secondary requirements:
|
# Secondary requirements:
|
||||||
asn1crypto==0.24.0
|
asn1crypto==0.24.0
|
||||||
|
156
setup.py
156
setup.py
@ -32,12 +32,14 @@ from distutils.command.install import install, INSTALL_SCHEMES
|
|||||||
from distutils.command.install_egg_info import install_egg_info
|
from distutils.command.install_egg_info import install_egg_info
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
requirement_re = re.compile('[><=]+')
|
requirement_re = re.compile("[><=]+")
|
||||||
sys.dont_write_bytecode = True
|
sys.dont_write_bytecode = True
|
||||||
|
|
||||||
# Download wheels from pypi. The specific versions are taken from requirements.txt
|
# Download wheels from pypi. The specific versions are taken from requirements.txt
|
||||||
wheels = [
|
wheels = [
|
||||||
'lockfile', 'pillarsdk', 'blender-asset-tracer',
|
"lockfile",
|
||||||
|
"pillarsdk",
|
||||||
|
"blender-asset-tracer",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -55,9 +57,9 @@ class BuildWheels(Command):
|
|||||||
|
|
||||||
description = "builds/downloads the dependencies as wheel files"
|
description = "builds/downloads the dependencies as wheel files"
|
||||||
user_options = [
|
user_options = [
|
||||||
('wheels-path=', None, "wheel file installation path"),
|
("wheels-path=", None, "wheel file installation path"),
|
||||||
('deps-path=', None, "path in which dependencies are built"),
|
("deps-path=", None, "path in which dependencies are built"),
|
||||||
('cachecontrol-path=', None, "subdir of deps-path containing CacheControl"),
|
("cachecontrol-path=", None, "subdir of deps-path containing CacheControl"),
|
||||||
]
|
]
|
||||||
|
|
||||||
def initialize_options(self):
|
def initialize_options(self):
|
||||||
@ -70,22 +72,23 @@ class BuildWheels(Command):
|
|||||||
self.my_path = pathlib.Path(__file__).resolve().parent
|
self.my_path = pathlib.Path(__file__).resolve().parent
|
||||||
package_path = self.my_path / self.distribution.get_name()
|
package_path = self.my_path / self.distribution.get_name()
|
||||||
|
|
||||||
self.wheels_path = set_default_path(self.wheels_path, package_path / 'wheels')
|
self.wheels_path = set_default_path(self.wheels_path, package_path / "wheels")
|
||||||
self.deps_path = set_default_path(self.deps_path, self.my_path / 'build/deps')
|
self.deps_path = set_default_path(self.deps_path, self.my_path / "build/deps")
|
||||||
self.cachecontrol_path = set_default_path(self.cachecontrol_path,
|
self.cachecontrol_path = set_default_path(
|
||||||
self.deps_path / 'cachecontrol')
|
self.cachecontrol_path, self.deps_path / "cachecontrol"
|
||||||
self.bat_path = self.deps_path / 'bat'
|
)
|
||||||
|
self.bat_path = self.deps_path / "bat"
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
log.info('Storing wheels in %s', self.wheels_path)
|
log.info("Storing wheels in %s", self.wheels_path)
|
||||||
|
|
||||||
# Parse the requirements.txt file
|
# Parse the requirements.txt file
|
||||||
requirements = {}
|
requirements = {}
|
||||||
with open(str(self.my_path / 'requirements.txt')) as reqfile:
|
with open(str(self.my_path / "requirements.txt")) as reqfile:
|
||||||
for line in reqfile.readlines():
|
for line in reqfile.readlines():
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
|
||||||
if not line or line.startswith('#'):
|
if not line or line.startswith("#"):
|
||||||
# comments are lines that start with # only
|
# comments are lines that start with # only
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -97,37 +100,45 @@ class BuildWheels(Command):
|
|||||||
|
|
||||||
self.wheels_path.mkdir(parents=True, exist_ok=True)
|
self.wheels_path.mkdir(parents=True, exist_ok=True)
|
||||||
for package in wheels:
|
for package in wheels:
|
||||||
pattern = package.replace('-', '_') + '*.whl'
|
pattern = package.replace("-", "_") + "*.whl"
|
||||||
if list(self.wheels_path.glob(pattern)):
|
if list(self.wheels_path.glob(pattern)):
|
||||||
continue
|
continue
|
||||||
self.download_wheel(requirements[package])
|
self.download_wheel(requirements[package])
|
||||||
|
|
||||||
# Build CacheControl.
|
# Build CacheControl.
|
||||||
if not list(self.wheels_path.glob('CacheControl*.whl')):
|
if not list(self.wheels_path.glob("CacheControl*.whl")):
|
||||||
log.info('Building CacheControl in %s', self.cachecontrol_path)
|
log.info("Building CacheControl in %s", self.cachecontrol_path)
|
||||||
# self.git_clone(self.cachecontrol_path,
|
# self.git_clone(self.cachecontrol_path,
|
||||||
# 'https://github.com/ionrock/cachecontrol.git',
|
# 'https://github.com/ionrock/cachecontrol.git',
|
||||||
# 'v%s' % requirements['CacheControl'][1])
|
# 'v%s' % requirements['CacheControl'][1])
|
||||||
# FIXME: we need my clone until pull request #125 has been merged & released
|
# FIXME: we need my clone until pull request #125 has been merged & released
|
||||||
self.git_clone(self.cachecontrol_path,
|
self.git_clone(
|
||||||
'https://github.com/sybrenstuvel/cachecontrol.git',
|
self.cachecontrol_path,
|
||||||
'sybren-filecache-delete-crash-fix')
|
"https://github.com/sybrenstuvel/cachecontrol.git",
|
||||||
|
"sybren-filecache-delete-crash-fix",
|
||||||
|
)
|
||||||
self.build_copy_wheel(self.cachecontrol_path)
|
self.build_copy_wheel(self.cachecontrol_path)
|
||||||
|
|
||||||
# Ensure that the wheels are added to the data files.
|
# Ensure that the wheels are added to the data files.
|
||||||
self.distribution.data_files.append(
|
self.distribution.data_files.append(
|
||||||
('blender_cloud/wheels', (str(p) for p in self.wheels_path.glob('*.whl')))
|
("blender_cloud/wheels", (str(p) for p in self.wheels_path.glob("*.whl")))
|
||||||
)
|
)
|
||||||
|
|
||||||
def download_wheel(self, requirement):
|
def download_wheel(self, requirement):
|
||||||
"""Downloads a wheel from PyPI and saves it in self.wheels_path."""
|
"""Downloads a wheel from PyPI and saves it in self.wheels_path."""
|
||||||
|
|
||||||
subprocess.check_call([
|
subprocess.check_call(
|
||||||
sys.executable, '-m', 'pip',
|
[
|
||||||
'download', '--no-deps',
|
sys.executable,
|
||||||
'--dest', str(self.wheels_path),
|
"-m",
|
||||||
requirement[0]
|
"pip",
|
||||||
])
|
"download",
|
||||||
|
"--no-deps",
|
||||||
|
"--dest",
|
||||||
|
str(self.wheels_path),
|
||||||
|
requirement[0],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def git_clone(self, workdir: pathlib.Path, git_url: str, checkout: str = None):
|
def git_clone(self, workdir: pathlib.Path, git_url: str, checkout: str = None):
|
||||||
if workdir.exists():
|
if workdir.exists():
|
||||||
@ -136,24 +147,25 @@ class BuildWheels(Command):
|
|||||||
|
|
||||||
workdir.mkdir(parents=True)
|
workdir.mkdir(parents=True)
|
||||||
|
|
||||||
subprocess.check_call(['git', 'clone', git_url, str(workdir)],
|
subprocess.check_call(
|
||||||
cwd=str(workdir.parent))
|
["git", "clone", git_url, str(workdir)], cwd=str(workdir.parent)
|
||||||
|
)
|
||||||
|
|
||||||
if checkout:
|
if checkout:
|
||||||
subprocess.check_call(['git', 'checkout', checkout],
|
subprocess.check_call(["git", "checkout", checkout], cwd=str(workdir))
|
||||||
cwd=str(workdir))
|
|
||||||
|
|
||||||
def build_copy_wheel(self, package_path: pathlib.Path):
|
def build_copy_wheel(self, package_path: pathlib.Path):
|
||||||
# Make sure no wheels exist yet, so that we know which one to copy later.
|
# Make sure no wheels exist yet, so that we know which one to copy later.
|
||||||
to_remove = list((package_path / 'dist').glob('*.whl'))
|
to_remove = list((package_path / "dist").glob("*.whl"))
|
||||||
for fname in to_remove:
|
for fname in to_remove:
|
||||||
fname.unlink()
|
fname.unlink()
|
||||||
|
|
||||||
subprocess.check_call([sys.executable, 'setup.py', 'bdist_wheel'],
|
subprocess.check_call(
|
||||||
cwd=str(package_path))
|
[sys.executable, "setup.py", "bdist_wheel"], cwd=str(package_path)
|
||||||
|
)
|
||||||
|
|
||||||
wheel = next((package_path / 'dist').glob('*.whl'))
|
wheel = next((package_path / "dist").glob("*.whl"))
|
||||||
log.info('copying %s to %s', wheel, self.wheels_path)
|
log.info("copying %s to %s", wheel, self.wheels_path)
|
||||||
shutil.copy(str(wheel), str(self.wheels_path))
|
shutil.copy(str(wheel), str(self.wheels_path))
|
||||||
|
|
||||||
|
|
||||||
@ -163,19 +175,19 @@ class BlenderAddonBdist(bdist):
|
|||||||
|
|
||||||
def initialize_options(self):
|
def initialize_options(self):
|
||||||
super().initialize_options()
|
super().initialize_options()
|
||||||
self.formats = ['zip']
|
self.formats = ["zip"]
|
||||||
self.plat_name = 'addon' # use this instead of 'linux-x86_64' or similar.
|
self.plat_name = "addon" # use this instead of 'linux-x86_64' or similar.
|
||||||
self.fix_local_prefix()
|
self.fix_local_prefix()
|
||||||
|
|
||||||
def fix_local_prefix(self):
|
def fix_local_prefix(self):
|
||||||
"""Place data files in blender_cloud instead of local/blender_cloud."""
|
"""Place data files in blender_cloud instead of local/blender_cloud."""
|
||||||
for key in INSTALL_SCHEMES:
|
for key in INSTALL_SCHEMES:
|
||||||
if 'data' not in INSTALL_SCHEMES[key]:
|
if "data" not in INSTALL_SCHEMES[key]:
|
||||||
continue
|
continue
|
||||||
INSTALL_SCHEMES[key]['data'] = '$base'
|
INSTALL_SCHEMES[key]["data"] = "$base"
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.run_command('wheels')
|
self.run_command("wheels")
|
||||||
super().run()
|
super().run()
|
||||||
|
|
||||||
|
|
||||||
@ -184,7 +196,7 @@ class BlenderAddonFdist(BlenderAddonBdist):
|
|||||||
"""Ensures that 'python setup.py fdist' creates a plain folder structure."""
|
"""Ensures that 'python setup.py fdist' creates a plain folder structure."""
|
||||||
|
|
||||||
user_options = [
|
user_options = [
|
||||||
('dest-path=', None, 'addon installation path'),
|
("dest-path=", None, "addon installation path"),
|
||||||
]
|
]
|
||||||
|
|
||||||
def initialize_options(self):
|
def initialize_options(self):
|
||||||
@ -198,12 +210,12 @@ class BlenderAddonFdist(BlenderAddonBdist):
|
|||||||
filepath = self.distribution.dist_files[0][2]
|
filepath = self.distribution.dist_files[0][2]
|
||||||
|
|
||||||
# if dest_path is not specified use the filename as the dest_path (minus the .zip)
|
# if dest_path is not specified use the filename as the dest_path (minus the .zip)
|
||||||
assert filepath.endswith('.zip')
|
assert filepath.endswith(".zip")
|
||||||
target_folder = self.dest_path or filepath[:-4]
|
target_folder = self.dest_path or filepath[:-4]
|
||||||
|
|
||||||
print('Unzipping the package on {}.'.format(target_folder))
|
print("Unzipping the package on {}.".format(target_folder))
|
||||||
|
|
||||||
with zipfile.ZipFile(filepath, 'r') as zip_ref:
|
with zipfile.ZipFile(filepath, "r") as zip_ref:
|
||||||
zip_ref.extractall(target_folder)
|
zip_ref.extractall(target_folder)
|
||||||
|
|
||||||
|
|
||||||
@ -213,8 +225,8 @@ class BlenderAddonInstall(install):
|
|||||||
|
|
||||||
def initialize_options(self):
|
def initialize_options(self):
|
||||||
super().initialize_options()
|
super().initialize_options()
|
||||||
self.prefix = ''
|
self.prefix = ""
|
||||||
self.install_lib = ''
|
self.install_lib = ""
|
||||||
|
|
||||||
|
|
||||||
class AvoidEggInfo(install_egg_info):
|
class AvoidEggInfo(install_egg_info):
|
||||||
@ -229,34 +241,38 @@ class AvoidEggInfo(install_egg_info):
|
|||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
cmdclass={'bdist': BlenderAddonBdist,
|
cmdclass={
|
||||||
'fdist': BlenderAddonFdist,
|
"bdist": BlenderAddonBdist,
|
||||||
'install': BlenderAddonInstall,
|
"fdist": BlenderAddonFdist,
|
||||||
'install_egg_info': AvoidEggInfo,
|
"install": BlenderAddonInstall,
|
||||||
'wheels': BuildWheels},
|
"install_egg_info": AvoidEggInfo,
|
||||||
name='blender_cloud',
|
"wheels": BuildWheels,
|
||||||
description='The Blender Cloud addon allows browsing the Blender Cloud from Blender.',
|
},
|
||||||
version='1.17',
|
name="blender_cloud",
|
||||||
author='Sybren A. Stüvel',
|
description="The Blender Cloud addon allows browsing the Blender Cloud from Blender.",
|
||||||
author_email='sybren@stuvel.eu',
|
version="1.25",
|
||||||
packages=find_packages('.'),
|
author="Sybren A. Stüvel",
|
||||||
|
author_email="sybren@stuvel.eu",
|
||||||
|
packages=find_packages("."),
|
||||||
data_files=[
|
data_files=[
|
||||||
('blender_cloud', ['README.md', 'README-flamenco.md', 'CHANGELOG.md']),
|
("blender_cloud", ["README.md", "README-flamenco.md", "CHANGELOG.md"]),
|
||||||
('blender_cloud/icons', glob.glob('blender_cloud/icons/*')),
|
("blender_cloud/icons", glob.glob("blender_cloud/icons/*")),
|
||||||
('blender_cloud/texture_browser/icons',
|
(
|
||||||
glob.glob('blender_cloud/texture_browser/icons/*'))
|
"blender_cloud/texture_browser/icons",
|
||||||
|
glob.glob("blender_cloud/texture_browser/icons/*"),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
scripts=[],
|
scripts=[],
|
||||||
url='https://developer.blender.org/diffusion/BCA/',
|
url="https://developer.blender.org/diffusion/BCA/",
|
||||||
license='GNU General Public License v2 or later (GPLv2+)',
|
license="GNU General Public License v2 or later (GPLv2+)",
|
||||||
platforms='',
|
platforms="",
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Intended Audience :: End Users/Desktop',
|
"Intended Audience :: End Users/Desktop",
|
||||||
'Operating System :: OS Independent',
|
"Operating System :: OS Independent",
|
||||||
'Environment :: Plugins',
|
"Environment :: Plugins",
|
||||||
'License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)',
|
"License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)",
|
||||||
'Programming Language :: Python',
|
"Programming Language :: Python",
|
||||||
'Programming Language :: Python :: 3.5',
|
"Programming Language :: Python :: 3.5",
|
||||||
],
|
],
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
)
|
)
|
||||||
|
@ -9,8 +9,8 @@ fi
|
|||||||
|
|
||||||
BL_INFO_VER=$(echo "$VERSION" | sed 's/\./, /g')
|
BL_INFO_VER=$(echo "$VERSION" | sed 's/\./, /g')
|
||||||
|
|
||||||
sed "s/version='[^']*'/version='$VERSION'/" -i setup.py
|
sed "s/version=\"[^\"]*\"/version=\"$VERSION\"/" -i setup.py
|
||||||
sed "s/'version': ([^)]*)/'version': ($BL_INFO_VER)/" -i blender_cloud/__init__.py
|
sed "s/\"version\": ([^)]*)/\"version\": ($BL_INFO_VER)/" -i blender_cloud/__init__.py
|
||||||
|
|
||||||
git diff
|
git diff
|
||||||
echo
|
echo
|
||||||
|
Reference in New Issue
Block a user