Skip to content

Conversation

kaywux
Copy link
Contributor

@kaywux kaywux commented Aug 25, 2023

Code example for python version of https://viam.atlassian.net/browse/DATA-1802

@viambot viambot added the safe to test This pull request is marked safe to test from a trusted zone label Aug 25, 2023
Pillow==10.0.0
protobuf==4.24.0
typing_extensions==4.7.1
/Users/katherinewu/viam/viam-python-sdk
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is necessary to pull in recent changes to the python SDK. Once these changes are included in a release this can be changed to viam-python-sdk

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Once viam-python-sdk changes have been merged, is all that's being installed then viam-python-sdk and then the package manager will handle all the implicit dependencies specified here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I believe so! At the end requirements.txt would just need to contain viam-python-sdk.

@kaywux kaywux marked this pull request as draft August 25, 2023 19:10
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Aug 30, 2023
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Aug 30, 2023
@kaywux kaywux marked this pull request as ready for review August 30, 2023 19:06
@kaywux kaywux requested a review from tahiyasalam August 30, 2023 19:07

# Filters the output of the underlying camera
async def get_image(self, mime_type: str = "", *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Image.Image:
if extra and extra["fromDataManagement"]:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if we want to have this fromDataManagement string as an importable variable in the SDKs as well? The only reason why I'm hesitating is because I don't want to add too many variables in the SDK that they then need to keep track of across languages, and because it would be easy for a user to debug any issues here (in contrast to NoCaptureToStoreError which is checked in the server code).

Copy link
Member

@tahiyasalam tahiyasalam Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, that's a great question. I see the validity of keeping track of this variable at the SDK level. Tbf, you are asking for hopefully only two variables to be kept track of. What is the process for keeping track of these "special" variables? Is it manual?

Copy link
Contributor Author

@kaywux kaywux Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it'll be manual for the SDK team to make sure these are consistent across sdks (currently just python and C++ since they're the only ones supporting modular components but will be more in the future) unless we build something special to track this, which falls outside the scope of this project. @benjirewis any thoughts on this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a ton of context here; are you asking whether this "'special" variable fromDataManagement that's currently just encoded in the extra field should be a full parameter for get_image? I suppose I would prefer an actual parameter (which would require an API change if I'm not mistaken), but if this was purely an internally-specified variable only used by the data management team than I could maybe see the justification for leaving it like this. If a user ever needs to specify fromDataManagement, then a full parameter seems pretty necessary IMO.

Copy link
Contributor Author

@kaywux kaywux Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a full parameter (which would indeed be an API change and I believe a lot of extra work on the sdk side since fromDM is currently designed to be passed through the extra map for all collectors), but rather I was thinking of a variable that users could import - something like what we can do with the go equivalent.

Copy link
Member

@cheukt cheukt Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flyby but I think adding something like from_dm_from_extra in a utils files might be valuable, like how we have opid_from_metadata in the python sdk. (in the sense that it's a bit manual and easy to get wrong but adding the helper reassures users that it's not that sketch and is the suggested way of accessing the var)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. If there's precedence in the Go module, I would also use a constant here. I see that Go uses a context key, but adding to the extra param seems ok for Python.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, thanks for the suggestions! I'll make a quick PR for this in the python SDK. And whoops sorry @benjirewis - I linked the wrong code pointer to my prev comment (fixed now)! The go camera is the only instance that needs to use the context key; the others should use extra[data.FromDMString]

@kaywux kaywux requested a review from dmhilly August 30, 2023 19:15
Copy link
Member

@tahiyasalam tahiyasalam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job putting this together. Some suggestions on my end and one question about whether or not we want to make environment decisions for the user.

Pillow==10.0.0
protobuf==4.24.0
typing_extensions==4.7.1
/Users/katherinewu/viam/viam-python-sdk
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Once viam-python-sdk changes have been merged, is all that's being installed then viam-python-sdk and then the package manager will handle all the implicit dependencies specified here?

VENV_NAME="venv"
PYTHON="$VENV_NAME/bin/python"

python3 -m venv $VENV_NAME
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is is a nice helper, but I actually feel like the details of how the user sets up their environment (like deciding what package manager they use, how they want to set up their environment) probably doesn't need to be defined and should be left up to them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense - this code was pretty much copied from the modular resource docs so can leave this file out of the example code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah actually thinking on this - I still feel like it could be good to have this here as a base so users have an example that works entirely out of the box that they can then edit to change the environment, etc. What do you think?

Copy link
Member

@tahiyasalam tahiyasalam Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, after you linked to the docs, I started thinking about this more as well. Since the module need to be executed, we can/probably should have this here. Can we add just add a new add the end of the file to say the shell script needs to be executable (you need to run sudo chmod +x run.sh)

# Filters the output of the underlying camera
async def get_image(self, mime_type: str = "", *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Image.Image:
if extra and extra["fromDataManagement"]:
img = await self.actual_cam.get_image()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small rearrangement: What do you think about moving the img = await self.actual_cam.get_image() to outside the conditional, then within the fromDataManagement check have if len(detections) == 0: raise NoCaptureToStoreError() and finally if none of those conditions are met having return img. I think this might emphasize when we're raising the error a bit better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, done!

from viam.services.vision import Vision

class ColorFilterCam(Camera, Reconfigurable):
# A ColorFilterCam wraps the underlying camera `actual_cam` and only keeps the data captured on the actual camera if `vision_service`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you use triple quotes """ for the comment, it will show up in the doc string, which is probably what you want for this comment that specifies what's happening within this class

async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> Camera.Properties:
return await self.actual_cam.get_properties()

# Filters the output of the underlying camera
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this and all other methods, we can move the comment to inside the function definition and use """. If you want to specify the args, you could do something like:

`""" Filters the output of the underlying camera

Args: mime_type: specifies the mime_type of the data from the underlying camera ...
"""`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would using """ turn this into a docstring like you mentioned above? These methods are already documented in the python SDK docs, not sure if we need to do that again here - I'll move the comments as they are inside the function definition, but let me know if that's not python-y!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would create docstring! I'm not sure if subclassing preserves docstrings from the superclass, but I would guess not. I do think it would be helpful to include the docstring if nothing else then for this method since this is the one thing that's different than the usual python SDK stuff. I think moving comments inside the function definition is python-y! :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, added docstrings to the implemented methods (the unimplemented ones are self-explanatory)


# Filters the output of the underlying camera
async def get_image(self, mime_type: str = "", *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Image.Image:
if extra and extra["fromDataManagement"]:
Copy link
Member

@tahiyasalam tahiyasalam Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, that's a great question. I see the validity of keeping track of this variable at the SDK level. Tbf, you are asking for hopefully only two variables to be kept track of. What is the process for keeping track of these "special" variables? Is it manual?

Copy link
Contributor Author

@kaywux kaywux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Python suggestions were super helpful, thanks Tahiya for the comments so far!

async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> Camera.Properties:
return await self.actual_cam.get_properties()

# Filters the output of the underlying camera
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would using """ turn this into a docstring like you mentioned above? These methods are already documented in the python SDK docs, not sure if we need to do that again here - I'll move the comments as they are inside the function definition, but let me know if that's not python-y!


# Filters the output of the underlying camera
async def get_image(self, mime_type: str = "", *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Image.Image:
if extra and extra["fromDataManagement"]:
Copy link
Contributor Author

@kaywux kaywux Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it'll be manual for the SDK team to make sure these are consistent across sdks (currently just python and C++ since they're the only ones supporting modular components but will be more in the future) unless we build something special to track this, which falls outside the scope of this project. @benjirewis any thoughts on this?

# Filters the output of the underlying camera
async def get_image(self, mime_type: str = "", *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Image.Image:
if extra and extra["fromDataManagement"]:
img = await self.actual_cam.get_image()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, done!

Pillow==10.0.0
protobuf==4.24.0
typing_extensions==4.7.1
/Users/katherinewu/viam/viam-python-sdk
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I believe so! At the end requirements.txt would just need to contain viam-python-sdk.

VENV_NAME="venv"
PYTHON="$VENV_NAME/bin/python"

python3 -m venv $VENV_NAME
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense - this code was pretty much copied from the modular resource docs so can leave this file out of the example code.

@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Aug 31, 2023
@kaywux
Copy link
Contributor Author

kaywux commented Aug 31, 2023

Adding @benjirewis to this PR since Naveed is OOO. Two main questions:

  1. Thoughts on adding fromDataManagement string to all SDKs for users to be able to import?
  2. I was looking at the python sdk code and saw that there were some examples in that repo as well. Should this example code live there or stay in the rdk repo with the go example?

@kaywux kaywux requested a review from benjirewis August 31, 2023 15:10
@tahiyasalam
Copy link
Member

Looking at the examples you linked to above -- I feel like this should go in the Python SDK examples

Copy link
Member

@benjirewis benjirewis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into this @kaywux ! After discussing a bit with @cheukt , we think this does not belong in the rdk. Could we possibly put this in viam labs instead along with some of other more complex example modules?


# Filters the output of the underlying camera
async def get_image(self, mime_type: str = "", *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Image.Image:
if extra and extra["fromDataManagement"]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a ton of context here; are you asking whether this "'special" variable fromDataManagement that's currently just encoded in the extra field should be a full parameter for get_image? I suppose I would prefer an actual parameter (which would require an API change if I'm not mistaken), but if this was purely an internally-specified variable only used by the data management team than I could maybe see the justification for leaving it like this. If a user ever needs to specify fromDataManagement, then a full parameter seems pretty necessary IMO.

@benjirewis
Copy link
Member

The Python SDK examples currently have the simple and complex examples, which we think is probably enough for now 🤔

@dmhilly dmhilly removed their request for review August 31, 2023 17:43
@dmhilly
Copy link
Member

dmhilly commented Aug 31, 2023

Taking myself off this one since it seems we have plenty of reviewers - feel free to add me back though @kaywux if you need another set of eyes!

@viambot viambot removed the safe to test This pull request is marked safe to test from a trusted zone label Aug 31, 2023
@viambot viambot added the safe to test This pull request is marked safe to test from a trusted zone label Aug 31, 2023
@kaywux
Copy link
Contributor Author

kaywux commented Aug 31, 2023

Updated the code to use the newly-added SDK util function (thanks @benjirewis for a quick review!) - once this PR is LGTM'ed I am planning to move this example, as well as the go example code that is already in rdk, to live in viam-labs.

@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Aug 31, 2023
Copy link
Member

@tahiyasalam tahiyasalam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing job, Katherine! This looks great.

Copy link
Member

@benjirewis benjirewis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM thanks for moving this over to viam labs! 🧑‍🔧

@github-actions
Copy link
Contributor

Code Coverage

Code Coverage
Package Line Rate Delta Health
go.viam.com/rdk/cli 4% 0.00%
go.viam.com/rdk/components/arm 59% 0.00%
go.viam.com/rdk/components/arm/fake 27% 0.00%
go.viam.com/rdk/components/arm/universalrobots 42% 0.00%
go.viam.com/rdk/components/arm/wrapper 19% 0.00%
go.viam.com/rdk/components/arm/xarm 22% 0.00%
go.viam.com/rdk/components/arm/yahboom 6% 0.00%
go.viam.com/rdk/components/audioinput 44% +0.47%
go.viam.com/rdk/components/base 60% 0.00%
go.viam.com/rdk/components/base/agilex 60% 0.00%
go.viam.com/rdk/components/base/kinematicbase 43% 0.00%
go.viam.com/rdk/components/base/sensorbase 68% 0.00%
go.viam.com/rdk/components/base/wheeled 89% 0.00%
go.viam.com/rdk/components/board 60% 0.00%
go.viam.com/rdk/components/board/customlinux 50% 0.00%
go.viam.com/rdk/components/board/fake 38% 0.00%
go.viam.com/rdk/components/board/genericlinux 8% 0.00%
go.viam.com/rdk/components/board/numato 19% 0.00%
go.viam.com/rdk/components/board/pi/impl 11% 0.00%
go.viam.com/rdk/components/camera 57% 0.00%
go.viam.com/rdk/components/camera/align 58% 0.00%
go.viam.com/rdk/components/camera/fake 74% 0.00%
go.viam.com/rdk/components/camera/ffmpeg 82% 0.00%
go.viam.com/rdk/components/camera/replaypcd 90% 0.00%
go.viam.com/rdk/components/camera/rtsp 52% 0.00%
go.viam.com/rdk/components/camera/transformpipeline 73% 0.00%
go.viam.com/rdk/components/camera/ultrasonic 61% 0.00%
go.viam.com/rdk/components/camera/videosource 39% 0.00%
go.viam.com/rdk/components/encoder 57% 0.00%
go.viam.com/rdk/components/encoder/ams 63% 0.00%
go.viam.com/rdk/components/encoder/fake 83% 0.00%
go.viam.com/rdk/components/encoder/incremental 80% 0.00%
go.viam.com/rdk/components/encoder/single 86% 0.00%
go.viam.com/rdk/components/gantry 60% 0.00%
go.viam.com/rdk/components/gantry/multiaxis 82% 0.00%
go.viam.com/rdk/components/gantry/singleaxis 81% 0.00%
go.viam.com/rdk/components/generic 79% 0.00%
go.viam.com/rdk/components/gripper 56% 0.00%
go.viam.com/rdk/components/input 88% 0.00%
go.viam.com/rdk/components/input/fake 93% 0.00%
go.viam.com/rdk/components/input/gpio 85% 0.00%
go.viam.com/rdk/components/motor 71% 0.00%
go.viam.com/rdk/components/motor/dimensionengineering 67% 0.00%
go.viam.com/rdk/components/motor/dmc4000 70% 0.00%
go.viam.com/rdk/components/motor/fake 55% 0.00%
go.viam.com/rdk/components/motor/gpio 73% 0.00%
go.viam.com/rdk/components/motor/gpiostepper 66% 0.00%
go.viam.com/rdk/components/motor/tmcstepper 53% 0.00%
go.viam.com/rdk/components/motor/ulnstepper 53% 0.00%
go.viam.com/rdk/components/movementsensor 74% 0.00%
go.viam.com/rdk/components/movementsensor/adxl345 75% 0.00%
go.viam.com/rdk/components/movementsensor/gpsnmea 55% 0.00%
go.viam.com/rdk/components/movementsensor/gpsrtkpmtk 21% 0.00%
go.viam.com/rdk/components/movementsensor/gpsrtkserial 28% 0.00%
go.viam.com/rdk/components/movementsensor/merged 91% 0.00%
go.viam.com/rdk/components/movementsensor/mpu6050 84% 0.00%
go.viam.com/rdk/components/movementsensor/replay 93% 0.00%
go.viam.com/rdk/components/movementsensor/rtkutils 24% 0.00%
go.viam.com/rdk/components/movementsensor/wheeledodometry 78% 0.00%
go.viam.com/rdk/components/posetracker 71% 0.00%
go.viam.com/rdk/components/powersensor 49% 0.00%
go.viam.com/rdk/components/sensor 51% 0.00%
go.viam.com/rdk/components/sensor/ultrasonic 38% 0.00%
go.viam.com/rdk/components/servo 62% 0.00%
go.viam.com/rdk/components/servo/gpio 72% 0.00%
go.viam.com/rdk/config 80% 0.00%
go.viam.com/rdk/control 57% 0.00%
go.viam.com/rdk/data 78% +4.48%
go.viam.com/rdk/examples/customresources/demos/remoteserver 0% 0.00%
go.viam.com/rdk/grpc 25% 0.00%
go.viam.com/rdk/internal/cloud 100% 0.00%
go.viam.com/rdk/ml 67% 0.00%
go.viam.com/rdk/ml/inference 67% 0.00%
go.viam.com/rdk/module 75% 0.00%
go.viam.com/rdk/module/modmanager 82% 0.00%
go.viam.com/rdk/motionplan 78% +0.32%
go.viam.com/rdk/motionplan/ik 68% 0.00%
go.viam.com/rdk/motionplan/tpspace 52% 0.00%
go.viam.com/rdk/operation 83% 0.00%
go.viam.com/rdk/pointcloud 67% 0.00%
go.viam.com/rdk/protoutils 49% 0.00%
go.viam.com/rdk/referenceframe 69% 0.00%
go.viam.com/rdk/resource 77% 0.00%
go.viam.com/rdk/rimage 55% 0.00%
go.viam.com/rdk/rimage/depthadapter 94% 0.00%
go.viam.com/rdk/rimage/transform 72% 0.00%
go.viam.com/rdk/rimage/transform/cmd/extrinsic_calibration 67% 0.00%
go.viam.com/rdk/robot 86% +0.87%
go.viam.com/rdk/robot/client 82% +0.18%
go.viam.com/rdk/robot/framesystem 36% 0.00%
go.viam.com/rdk/robot/impl 83% 0.00%
go.viam.com/rdk/robot/packages 75% 0.00%
go.viam.com/rdk/robot/server 55% 0.00%
go.viam.com/rdk/robot/web 65% 0.00%
go.viam.com/rdk/robot/web/stream 87% 0.00%
go.viam.com/rdk/services/baseremotecontrol 50% 0.00%
go.viam.com/rdk/services/baseremotecontrol/builtin 81% 0.00%
go.viam.com/rdk/services/datamanager 65% 0.00%
go.viam.com/rdk/services/datamanager/builtin 89% 0.00%
go.viam.com/rdk/services/datamanager/datacapture 73% 0.00%
go.viam.com/rdk/services/datamanager/datasync 0% 0.00%
go.viam.com/rdk/services/mlmodel 76% 0.00%
go.viam.com/rdk/services/mlmodel/tflitecpu 85% 0.00%
go.viam.com/rdk/services/motion 52% 0.00%
go.viam.com/rdk/services/motion/builtin 81% 0.00%
go.viam.com/rdk/services/navigation 47% 0.00%
go.viam.com/rdk/services/navigation/builtin 87% 0.00%
go.viam.com/rdk/services/sensors 81% 0.00%
go.viam.com/rdk/services/sensors/builtin 97% 0.00%
go.viam.com/rdk/services/shell 11% 0.00%
go.viam.com/rdk/services/slam 73% 0.00%
go.viam.com/rdk/services/slam/fake 82% 0.00%
go.viam.com/rdk/services/vision 34% 0.00%
go.viam.com/rdk/services/vision/colordetector 56% 0.00%
go.viam.com/rdk/services/vision/detectionstosegments 67% 0.00%
go.viam.com/rdk/services/vision/mlvision 65% +0.27%
go.viam.com/rdk/services/vision/obstaclesdepth 74% 0.00%
go.viam.com/rdk/services/vision/obstaclesdistance 77% 0.00%
go.viam.com/rdk/services/vision/obstaclespointcloud 59% 0.00%
go.viam.com/rdk/session 94% 0.00%
go.viam.com/rdk/spatialmath 82% 0.00%
go.viam.com/rdk/utils 65% 0.00%
go.viam.com/rdk/utils/contextutils 38% 0.00%
go.viam.com/rdk/vision 37% 0.00%
go.viam.com/rdk/vision/delaunay 87% 0.00%
go.viam.com/rdk/vision/keypoints 92% 0.00%
go.viam.com/rdk/vision/objectdetection 69% 0.00%
go.viam.com/rdk/vision/segmentation 54% 0.00%
go.viam.com/rdk/web/server 26% 0.00%
Summary 62% (23184 / 37619) +0.05%

@kaywux
Copy link
Contributor Author

kaywux commented Sep 1, 2023

Thanks for the reviews on this! Closing this PR as the code here has been added to https://github.com/viam-labs/modular-filter-examples.

@kaywux kaywux closed this Sep 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
safe to test This pull request is marked safe to test from a trusted zone
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants