Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lint the OpenCue Python libraries. #890

Merged
merged 8 commits into from
Feb 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/pylintrc_test
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ disable=duplicate-code,
no-self-use,
protected-access,
raise-missing-from,
too-many-locals,
too-many-public-methods,
unused-argument,
unused-variable,
Expand Down
4 changes: 4 additions & 0 deletions ci/run_python_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ python rqd/setup.py test

# Some environments don't have pylint available, for ones that do they should pass this flag.
if [[ "$1" == "--lint" ]]; then
cd pycue && python -m pylint --rcfile=../ci/pylintrc_main FileSequence && cd ..
cd pycue && python -m pylint --rcfile=../ci/pylintrc_main opencue --ignore=opencue/compiled_proto && cd ..
cd pycue && python -m pylint --rcfile=../ci/pylintrc_test tests && cd ..

cd pyoutline && PYTHONPATH=../pycue python -m pylint --rcfile=../ci/pylintrc_main outline && cd ..
cd pyoutline && PYTHONPATH=../pycue python -m pylint --rcfile=../ci/pylintrc_test tests && cd ..

Expand Down
49 changes: 33 additions & 16 deletions pycue/FileSequence/FrameRange.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Helper class for representing a frame range.

It supports a complex syntax implementing features such as comma-separated frame ranges,
stepped frame ranges and more. See the FrameRange class for more detail.
"""

from __future__ import division
from __future__ import print_function
Expand All @@ -25,11 +31,12 @@


class FrameRange(object):
"""Represents a sequence of image frames."""
"""Represents a sequence of frame numbers."""

SINGLE_FRAME_PATTERN = re.compile(r'^(-?)\d+$')
SIMPLE_FRAME_RANGE_PATTERN = re.compile(r'^(?P<sf>(-?)\d+)-(?P<ef>(-?)\d+)$')
STEP_PATTERN = re.compile(r'^(?P<sf>(-?)\d+)-(?P<ef>(-?)\d+)(?P<stepSep>[xy])(?P<step>(-?)\d+)$')
STEP_PATTERN = re.compile(
r'^(?P<sf>(-?)\d+)-(?P<ef>(-?)\d+)(?P<stepSep>[xy])(?P<step>(-?)\d+)$')
INTERLEAVE_PATTERN = re.compile(r'^(?P<sf>(-?)\d+)-(?P<ef>(-?)\d+):(?P<step>(-?)\d+)$')

def __init__(self, frameRange):
Expand Down Expand Up @@ -103,11 +110,20 @@ def getAll(self):
return self.frameList

def normalize(self):
"""Sorts and deduplicates the sequence."""
self.frameList = list(set(self.frameList))
self.frameList.sort()

@classmethod
def parseFrameRange(cls, frameRange):
"""
Parse a string representation into a numerical sequence.

:type frameRange: str
:param frameRange: String representation of the frame range.
:rtype: FrameRange
:return: FrameRange representing the numerical sequence.
"""
singleFrameMatcher = re.match(cls.SINGLE_FRAME_PATTERN, frameRange)
if singleFrameMatcher:
return [int(frameRange)]
Expand All @@ -116,56 +132,57 @@ def parseFrameRange(cls, frameRange):
if simpleRangeMatcher:
startFrame = int(simpleRangeMatcher.group('sf'))
endFrame = int(simpleRangeMatcher.group('ef'))
return cls.getIntRange(startFrame, endFrame, (1 if endFrame >= startFrame else -1))
return cls.__getIntRange(startFrame, endFrame, (1 if endFrame >= startFrame else -1))

rangeWithStepMatcher = re.match(cls.STEP_PATTERN, frameRange)
if rangeWithStepMatcher:
startFrame = int(rangeWithStepMatcher.group('sf'))
endFrame = int(rangeWithStepMatcher.group('ef'))
step = int(rangeWithStepMatcher.group('step'))
stepSep = rangeWithStepMatcher.group('stepSep')
return cls.getSteppedRange(startFrame, endFrame, step, stepSep == 'y')
return cls.__getSteppedRange(startFrame, endFrame, step, stepSep == 'y')

rangeWithInterleaveMatcher = re.match(cls.INTERLEAVE_PATTERN, frameRange)
if rangeWithInterleaveMatcher:
startFrame = int(rangeWithInterleaveMatcher.group('sf'))
endFrame = int(rangeWithInterleaveMatcher.group('ef'))
step = int(rangeWithInterleaveMatcher.group('step'))
return cls.getInterleavedRange(startFrame, endFrame, step)
return cls.__getInterleavedRange(startFrame, endFrame, step)

raise ValueError('unrecognized frame range syntax ' + frameRange)

@staticmethod
def getIntRange(start, end, step):
def __getIntRange(start, end, step):
return list(range(start, end+(step // abs(step)), step))

@classmethod
def getSteppedRange(cls, start, end, step, inverseStep):
cls.validateStepSign(start, end, step)
steppedRange = cls.getIntRange(start, end, step)
def __getSteppedRange(cls, start, end, step, inverseStep):
cls.__validateStepSign(start, end, step)
steppedRange = cls.__getIntRange(start, end, step)
if inverseStep:
fullRange = cls.getIntRange(start, end, (-1 if step < 0 else 1))
fullRange = cls.__getIntRange(start, end, (-1 if step < 0 else 1))
return [frame for frame in fullRange if frame not in steppedRange]
return steppedRange

@classmethod
def getInterleavedRange(cls, start, end, step):
cls.validateStepSign(start, end, step)
def __getInterleavedRange(cls, start, end, step):
cls.__validateStepSign(start, end, step)
interleavedFrames = OrderedDict()
incrValue = step // abs(step)
while abs(step) > 0:
interleavedFrames.update([(frame, None) for frame in cls.getIntRange(start, end, step)])
interleavedFrames.update(
[(frame, None) for frame in cls.__getIntRange(start, end, step)])
start += incrValue
step = int(step / 2.0)
return list(interleavedFrames.keys())

@staticmethod
def validateStepSign(start, end, step):
def __validateStepSign(start, end, step):
if step > 1 and end < start:
raise ValueError(
'end frame may not be less than start frame when using a positive step')
elif step == 0:
if step == 0:
raise ValueError('step cannot be zero')
elif step < 0 and end >= start:
if step < 0 and end >= start:
raise ValueError(
'end frame may not be greater than start frame when using a negative step')
12 changes: 11 additions & 1 deletion pycue/FileSequence/FrameSet.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module for `FileSequence.FrameSet`."""

from __future__ import absolute_import
from __future__ import print_function
Expand All @@ -23,7 +24,7 @@


class FrameSet(object):
"""Represents a sequence of FrameRanges."""
"""Represents a sequence of `FileSequence.FrameRange`."""

def __init__(self, frameRange):
"""Construct a FrameSet object by parsing a spec.
Expand Down Expand Up @@ -67,11 +68,20 @@ def getAll(self):
return self.frameList

def normalize(self):
"""Sorts and dedeuplicates the sequence."""
self.frameList = list(set(self.frameList))
self.frameList.sort()

@staticmethod
def parseFrameRange(frameRange):
"""
Parses a string representation of a frame range into a FrameSet.

:type frameRange: str
:param frameRange: String representation of the frame range.
:rtype: FrameSet
:return: The FrameSet representing the same sequence.
"""
frameList = list()
for frameRangeSection in frameRange.split(','):
frameList.extend(FrameRange.parseFrameRange(frameRangeSection))
Expand Down
20 changes: 20 additions & 0 deletions pycue/FileSequence/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
# Copyright Contributors to the OpenCue Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Top-level module of FileSequence.

FileSequence contains helper classes for representing a job's frame range.
"""

from __future__ import absolute_import
from __future__ import print_function
from __future__ import division
Expand Down
4 changes: 4 additions & 0 deletions pycue/opencue/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,25 @@
# See the License for the specific language governing permissions and
# limitations under the License.

"""Top level of the opencue module."""

from __future__ import absolute_import
from __future__ import print_function
from __future__ import division

import logging

# pylint: disable=cyclic-import
from .cuebot import Cuebot
from . import api
from . import wrappers
from . import search

from .exception import CueException
from .exception import EntityNotFoundException
# pylint: disable=redefined-builtin
from .util import id
# pylint: enable=redefined-builtin
from .util import logPath
from .util import proxy
from .util import rep
Expand Down
59 changes: 42 additions & 17 deletions pycue/opencue/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.

"""The OpenCue static API."""


"""
The opencue Static API. This is exported into the package namespace.

Project: opencue Library
"""
from __future__ import absolute_import
from __future__ import print_function
from __future__ import division

from . import search
from . import util
from opencue.compiled_proto import comment_pb2
from opencue.compiled_proto import criterion_pb2
from opencue.compiled_proto import cue_pb2
Expand All @@ -42,6 +35,7 @@
from opencue.compiled_proto import subscription_pb2
from opencue.compiled_proto import task_pb2
from .cuebot import Cuebot
# pylint: disable=cyclic-import
from .wrappers.allocation import Allocation
from .wrappers.comment import Comment
from .wrappers.depend import Depend
Expand All @@ -60,11 +54,13 @@
from .wrappers.show import Show
from .wrappers.subscription import Subscription
from .wrappers.task import Task
from . import search
from . import util


__protobufs = [comment_pb2, criterion_pb2, cue_pb2, department_pb2, depend_pb2, facility_pb2,
filter_pb2, host_pb2, job_pb2, renderPartition_pb2, report_pb2, service_pb2, show_pb2,
subscription_pb2, task_pb2]
filter_pb2, host_pb2, job_pb2, renderPartition_pb2, report_pb2, service_pb2,
show_pb2, subscription_pb2, task_pb2]

__wrappers = [Action, Allocation, Comment, Depend, Filter, Frame, Group, Host, Job, Layer, Matcher,
NestedHost, Proc, Show, Subscription, Task]
Expand Down Expand Up @@ -319,7 +315,7 @@ def getJobs(**options):
For example::

# returns only pipe jobs.
getJobs(show=["pipe"])
getJobs(show=["pipe"])

Possible args:
- job: job names - list
Expand Down Expand Up @@ -391,8 +387,6 @@ def getJobNames(**options):
"""Returns a list of job names that match the search parameters.
See getJobs for the job query options.

:type options: dict
:param options: a variable list of search criteria
:rtype: list
:return: List of matching str job names"""
criteria = search.JobSearch.criteriaFromOptions(**options)
Expand Down Expand Up @@ -557,15 +551,15 @@ def getHost(uniq):
# Owners
#
@util.grpcExceptionParser
def getOwner(id):
def getOwner(owner_id):
"""Return an Owner object from the ID or name.

:type id: str
:param id: a unique owner identifier or name
:type owner_id: str
:param owner_id: a unique owner identifier or name
:rtype: Owner
:return: An Owner object"""
return Owner(Cuebot.getStub('owner').GetOwner(
host_pb2.OwnerGetOwnerRequest(name=id), timeout=Cuebot.Timeout).owner)
host_pb2.OwnerGetOwnerRequest(name=owner_id), timeout=Cuebot.Timeout).owner)

#
# Filters
Expand Down Expand Up @@ -640,25 +634,56 @@ def getAllocation(allocId):

@util.grpcExceptionParser
def deleteAllocation(alloc):
"""Deletes an allocation.

:type alloc: facility_pb2.Allocation
:param alloc: allocation to delete
:rtype: facility_pb2.AllocDeleteResponse
:return: empty response"""
return Cuebot.getStub('allocation').Delete(
facility_pb2.AllocDeleteRequest(allocation=alloc), timeout=Cuebot.Timeout)


@util.grpcExceptionParser
def allocSetBillable(alloc, is_billable):
"""Sets an allocation billable or not.

:type alloc: facility_pb2.Allocation
:param alloc: allocation to set
:type is_billable: bool
:param is_billable: whether alloc should be billable or not
:rtype: facility_pb2.AllocSetBillableResponse
:return: empty response
"""
return Cuebot.getStub('allocation').SetBillable(
facility_pb2.AllocSetBillableRequest(allocation=alloc, value=is_billable),
timeout=Cuebot.Timeout)


@util.grpcExceptionParser
def allocSetName(alloc, name):
"""Sets an allocation name.

:type alloc: facility_pb2.Allocation
:param alloc: allocation to set
:type name: str
:param name: new name for the allocation
:rtype: facility_pb2.AllocSetNameResponse
:return: empty response"""
return Cuebot.getStub('allocation').SetName(
facility_pb2.AllocSetNameRequest(allocation=alloc, name=name), timeout=Cuebot.Timeout)


@util.grpcExceptionParser
def allocSetTag(alloc, tag):
"""Sets an allocation tag.

:type alloc: facility_pb2.Allocation
:param alloc: allocation to tag
:type tag: str
:param tag: new tag
:rtype: facility_pb2.AllocSetTagResponse
:return: empty response"""
return Cuebot.getStub('allocation').SetTag(
facility_pb2.AllocSetTagRequest(allocation=alloc, tag=tag), timeout=Cuebot.Timeout)

Expand Down
Loading