-
Notifications
You must be signed in to change notification settings - Fork 320
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
Driver for Cryogenic sms120c #819
Driver for Cryogenic sms120c #819
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a great driver for a highly non-standard instrument (gotta love the if m[2] == '------->':
).
A few issues on style:
- Delete outcommented lines of code
- Use module level logging (
log = logging.getLogger(__name__)
) and changeprint
tolog.error
,log.warning
, orlog.debug
where it is appropriate
Optionally, we would be happy if you would use type hints (including return value) for the function.
Is it correct that the parameters can't have units because the instrument swicthes between "Ampere state" and "Tesla state"? If not, the parameters should have a unit
.
Hi William,
Thanks for the feedback! I'll make the changes and resubmit.
Yes the instrument lets you swap units, so you can run it with Amps or
Tesla, though we tend to prefer Tesla.
Best regards,
LaReine.
…On Oct 27, 2017 10:30 AM, "William H.P. Nielsen" ***@***.***> wrote:
***@***.**** requested changes on this pull request.
Looks like a great driver for a highly non-standard instrument (gotta love
the if m[2] == '------->':).
A few issues on style:
- Delete outcommented lines of code
- Use module level logging (log = logging.getLogger(__name__)) and
change print to log.error, log.warning, or log.debug where it is
appropriate
Optionally, we would be happy if you would use type hints (including
return value) for the function.
Is it correct that the parameters can't have units because the instrument
swicthes between "Ampere state" and "Tesla state"? If not, the parameters
should have a unit.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#819 (review)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AURkE7ZnUYOecPlqYEkK1mvlqod6nD_Dks5swZScgaJpZM4QH7jL>
.
|
@WilliamHPNielsen Driver now passes tests. @layeoh Can you test the driver? |
Okay great, I'll do a pull on the 'up' branch to sync the changes and test
the magnet with the edited code tomorrow in the lab.
Thanks for all the help and feedback too by the way!
…On Oct 29, 2017 9:12 PM, "peendebak" ***@***.***> wrote:
@WilliamHPNielsen <https://github.com/williamhpnielsen> Driver now passes
tests. @layeoh <https://github.com/layeoh> Can you test the driver?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#819 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AURkEwsiNx4TsXsfzsVKlsUxvpNMml5Jks5sxNxGgaJpZM4QH7jL>
.
|
# Created on Fri 29 Nov 2017 | ||
# @author: lyeoh | ||
|
||
# Last modified by lyeoh 27/10/2017 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's kind of redundant to maintain such information in the file since we are using git.
if self._get_rampRate() <= self._current_ramp_limit: | ||
if state == 2: # Quench | ||
log.error( | ||
__name__ + ': Magnet quench detected - please check magnet status before ramping.') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SInce you are now using module level logging (log = logging.getLogger(__name__)
), there is no reason to prepend the __name__
.
@@ -71,6 +71,7 @@ def __init__(self, name, address, coil_constant=0.113375, current_rating=105.84, | |||
idn = self.IDN.get() | |||
print(idn) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be handled by the connect_message
method of the instrument. Unless the IDN response is too non-standard.
units = self._get_unit() | ||
if units == 1: | ||
print("Magnet in persistent mode, at a field of %f T" % field) | ||
print("Magnet in persistent mode, at a field of %f T" % |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This print call (and many more below in the driver) should be converted to logging calls with the appropriate level.
…Qutech/Qcodes into feat/crygenicSMS120c
Hi !
I have updated the print statements with the log.info as suggested. These
statements are important feedback to the user about the state of the
magnet, hence the need to print these to the command line.
The idn response is quite non standard so it was easier to place it here.
The format ive used is similar to that of the ami430 vector magnet driver.
Another advantage is perhaps if a different setup uses the same power
supply but a different magnet, the next person can modify these properties
easily for the magnet and still re-use the driver, as is/minimal
modification, to communicate with the power supply.
…On Nov 16, 2017 9:41 AM, "William H.P. Nielsen" ***@***.***> wrote:
***@***.**** requested changes on this pull request.
------------------------------
In qcodes/instrument_drivers/cryogenic/CryogenicSMS120C.py
<#819 (comment)>:
> @@ -71,6 +71,7 @@ def __init__(self, name, address, coil_constant=0.113375, current_rating=105.84,
idn = self.IDN.get()
print(idn)
This should be handled by the connect_message method of the instrument.
Unless the IDN response is too non-standard.
------------------------------
In qcodes/instrument_drivers/cryogenic/CryogenicSMS120C.py
<#819 (comment)>:
> units = self._get_unit()
if units == 1:
- print("Magnet in persistent mode, at a field of %f T" % field)
+ print("Magnet in persistent mode, at a field of %f T" %
This print call (and many more below in the driver) should be converted to
logging calls with the appropriate level.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#819 (review)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AURkE2hSUq3-NQeSELps2N-nYoE0nMN3ks5s2_VDgaJpZM4QH7jL>
.
|
@WilliamHPNielsen Driver should be fine now. |
@layeoh sorry, but I don't really see the requested changes implemented? Perhaps I am missing something, but it looks like you simply left all calls to print as they were? In short, there should not be any print calls unless in functions that explicitly request it such as Finally, I don't understand the purpose of setting the log level to info sometimes. Can you explain the idea to me? |
Hi William,
Sorry I think there was a conflict in one of the branches and the update
wasn't pushed. Pieter has kindly fixed that, so it should now be the right
file.
I wanted the log set to info in this case to catch/record these events,
since these messages are not programming/communication related, but are
more to do with the operation of the magnet and hardware.
In some cases like moving in and out of persistent mode, it may take some
time, and the user may be wondering what is happening if there is no
feedback. Usually magnet PS controller should handle this particular type
of basic operation e.g. Switch heater timing for this, but this old model
doesn't, so I've added the extra function into the driver for safety/(to
ensure correct operation) as it's something that is commonly used in our
experiments (and would like to call in an automated routine).
Especially when working with equipment like larger superconducting magnets,
at full field there is a lot of energy stored/generated, so i prefer the
option to get some feedback on the magnet state, at critical points during
operation.
This log.info 'lower setting' is also optional (can be switched off,
reverted back to normal), for other users who do not require it.
…On Nov 21, 2017 9:31 AM, "William H.P. Nielsen" ***@***.***> wrote:
@layeoh <https://github.com/layeoh> sorry, but I don't really see the
requested changes implemented? Perhaps I am missing something, but it looks
like you simply left all calls to print as they were?
In short, there should not be any print calls unless in functions that
*explicitly* request it such as print_readable_snapshot or
print_error_messages. There should, however, always be logging of such
important feedback as "magnet currently holding" etc. It is not up to
driver to decide how the user should handle this information, the driver
should simply make it available to the user. And printed messages are not
available in all circumstances. If somebody is writing automated procedures
using an instrument, the output of print is basically lost to them, whereas
the logging system messages are easily retrieved. The driver should not
make the assumption that users always sit at a terminal and execute driver
commands one-by-one.
Finally, I don't understand the purpose of setting the log level to info
sometimes. Can you explain the idea to me?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#819 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AURkE17dgusHkgSHh32jWqWaol5jsP-cks5s4opTgaJpZM4QH7jL>
.
|
@layeoh I'm glad we agree about the usefulness of logging over prints! And I can fully get behind always informing about what is happening in the driver, especially if it takes more than a few ms (like moving into persistent mode). I don't quite understand the way you handle the logging level, though. Basically, I would say that one should never set the logging levels in the driver. If you don't set any levels, all log messages are piped on in the system. Filtering should happen later by setting the level of loghandlers and/or the loggers at runtime. I plainly don't understand how setting the loglevel to |
Hi William,
I'm new to both Python and Qcodes so I'm probably not the best person to
answer the part about logging (writing this driver is my introduction to
both).
I do understand what you mean about the log level handling, and I agree
it's certainly not ideal to put this in the driver, but it was the fastest
option I could think of at the time of writing the code.
At the moment the way logging is handled is just the default python output
errors to the command prompt - so by default it only prints warnings and
anything more critical than that. (we don't yet have a dedicated logging
handler that I can see).
So for me to see any log.info output I have to set this level every time.
But since we have a lot of routines and instruments, I am reluctant to put
it higher up in the code otherwise I might get a flood of messages - I just
wanted the log.info level set for this magnet driver only, without
disturbing the rest of the workflow (the magnet driver will probably get
called at different places in the code by different routines, depending on
who is using it).
If you have any ideas or a better way to discriminate log level plus its
source - or perhaps in general how to handle/approach this particular case,
please let me know.
- Otherwise it'll will be a lot of setting/unsetting log levels in the
experiment scripts, which can get messy quite quickly.
…On Nov 22, 2017 11:33 AM, "William H.P. Nielsen" ***@***.***> wrote:
@layeoh <https://github.com/layeoh> I'm glad we agree about the
usefulness of logging over prints! And I can fully get behind always
informing about what is happening in the driver, especially if it takes
more than a few ms (like moving into persistent mode).
I don't quite understand the way you handle the logging level, though.
Basically, I would say that one should never set the logging levels in the
driver. If you don't set any levels, all log messages are piped on in the
system. Filtering should happen later by setting the level of loghandlers
and/or the loggers at runtime.
I plainly don't understand how setting the loglevel to INFO changes
anything. Do you actually not get these messages if you don't do that? How
do you usually read out logging information during measurement operation?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#819 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AURkE5FIcEhldnanrCkteQqFSeOnJ7LNks5s4_h3gaJpZM4QH7jL>
.
|
@layeoh That's a pretty impressive first driver! Regarding the handling of logging messages, I would recommend the following:
logger = logging.getLogger('<name-of-driver-module>') # the name is probably 'qcodes/instrument_drivers/cryogenic/CryogenicSMS120C'
handler = logging.StreamHandler(sys.stdout)
logger.setLevel(logging.INFO) # set the logger's level
handler.setLevel(logging.INFO) # here you get a second chance to discriminate
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler) It's probably a bit longer than what you are doing now, but I think that this is more correct. It's also easy to modify to log to a file plus you get to set the format which can be extremely helpful, e.g. by adding information on which function is producing the message (check out https://docs.python.org/3/library/logging.html#logrecord-attributes to see what' spossible). Does the above help you/make sense? |
@WilliamHPNielsen @layeoh The lines of code above will print each
to the driver init? |
@peendebak It will only print twice if your root logger (or any parent of the logger) has a handler that prints. But if that is the case, then you should be home safe by simply doing this in your script/notebook: logger = logging.getLogger('<name-of-driver-module>') # the name is probably 'qcodes/instrument_drivers/cryogenic/CryogenicSMS120C'
logger.setLevel(logging.INFO) Does that work for you? @peendebak / @layeoh |
@WilliamHPNielsen i've made the changes with @peendebak today. Thank you for your suggestions,
modified the driver module name in the experiment script for it to work, and the write to file option is very handy!
…On Nov 23, 2017 2:54 PM, "William H.P. Nielsen" ***@***.***> wrote:
@peendebak <https://github.com/peendebak> It will only print twice if
your root logger has a handler (that prints). But if that is the case, then
you should be home safe by simply doing this in your script/notebook:
logger = logging.getLogger('<name-of-driver-module>') # the name is probably 'qcodes/instrument_drivers/cryogenic/CryogenicSMS120C'
logger.setLevel(logging.INFO)
Does that work for you? @peendebak <https://github.com/peendebak> /
@layeoh <https://github.com/layeoh>
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#819 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AURkE8GGYynRtOUdJPFnB5rBSZmztkd_ks5s5XkDgaJpZM4QH7jL>
.
|
Looks good to me, I'll just wait for travis and then merge. Thanks for the discussion about logging! |
Author: peendebak <[email protected]> Driver for Cryogenic sms120c (#819)
Great! Thanks very much for your help and feedback @WilliamHPNielsen and @peendebak ! |
3746: Bump types-docutils from 0.17.1 to 0.17.2 r=jenshnielsen a=dependabot[bot] Bumps [types-docutils](https://github.com/python/typeshed) from 0.17.1 to 0.17.2. <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/python/typeshed/commits">compare view</a></li> </ul> </details> <br /> [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=types-docutils&package-manager=pip&previous-version=0.17.1&new-version=0.17.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) You can trigger a rebase of this PR by commenting ``@dependabot` rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - ``@dependabot` rebase` will rebase this PR - ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it - ``@dependabot` merge` will merge this PR after your CI passes on it - ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it - ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging - ``@dependabot` reopen` will reopen this PR if it is closed - ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> 3747: Bump attrs from 21.2.0 to 21.3.0 r=jenshnielsen a=dependabot[bot] Bumps [attrs](https://github.com/python-attrs/attrs) from 21.2.0 to 21.3.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/python-attrs/attrs/releases">attrs's releases</a>.</em></p> <blockquote> <h2>21.3.0</h2> <p>This is a big release in the history of <code>attrs</code> and finishes an arc that took way too long and also delayed this very overdue release. But it's done: <code>import attrs</code> that has been talked about for years[^issue], but fell victim to “just this one more thing” has <em>finally</em> landed.</p> <p>From now on, modern <code>attrs</code> code looks like this:</p> <pre lang="python"><code>from attrs import define <p><a href="https://github.com/define"><code>`@define</code></a>` class HelloWorld: modern: bool = True </code></pre></p> <p>The <code>define</code>/<code>field</code> APIs have been around for over a year and were very popular, now the rest of the package followed suit. I'm very excited that <code>attrs</code> remains relevant and keeps evolving over now more than half a decade. If you're curious about some of the background, the docs now contain a short <a href="https://www.attrs.org/en/stable/names.html">explanation and history lesson</a>. As long as our users keep pushing us, we will keep pushing forward class generation in Python!</p> <p>Big thanks to my <a href="https://github.com/sponsors/hynek/dashboard">GitHub Sponsors</a>, <a href="https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=referral&utm_campaign=enterprise&utm_term=repo">Tidelift subscribers</a>, and <a href="https://ko-fi.com/the_hynekl">Ko-fi buyers</a> that help me mustering the motivation for such long-running project!</p> <hr /> <p>Since the release took so long, there's more highlights than we can enumerate here, we'd just like to point out a breaking change in the new APIs: converters now run on setting attributes by default. If this is causing problems to you, you can disable that behavior by setting <code>`@define(on_setattr=[])</code>.</p>` <p>[^issue]: I have an issue from 2018 that I wanted to "come back the moment this lands".</p> <h1>Full Changelog</h1> <h2>Backward-incompatible Changes</h2> <ul> <li> <p>When using <code>`@define</code>,` converters are now run by default when setting an attribute on an instance -- additionally to validators. I.e. the new default is <code>on_setattr=[attrs.setters.convert, attrs.setters.validate]</code>.</p> <p>This is unfortunately a breaking change, but it was an oversight, impossible to raise a <code>DeprecationWarning</code> about, and it's better to fix it now while the APIs are very fresh with few users. <a href="https://github-redirect.dependabot.com/python-attrs/attrs/issues/835">#835</a>, <a href="https://github-redirect.dependabot.com/python-attrs/attrs/issues/886">#886</a></p> </li> <li> <p><code>import attrs</code> has finally landed! As of this release, you can finally import <code>attrs</code> using its proper name.</p> <p>Not all names from the <code>attr</code> namespace have been transferred; most notably <code>attr.s</code> and <code>attr.ib</code> are missing. See <code>attrs.define</code> and <code>attrs.field</code> if you haven't seen our next-generation APIs yet. A more elaborate explanation can be found <a href="https://www.attrs.org/en/latest/names.html">On The Core API Names</a></p> <p>This feature is at least for one release <strong>provisional</strong>. We don't <em>plan</em> on changing anything, but such a big change is unlikely to go perfectly on the first strike.</p> <p>The API docs have been mostly updated, but it will be an ongoing effort to change everything to the new APIs. Please note that we have <strong>not</strong> moved -- or even removed -- anything from <code>attr</code>!</p> <p>Please do report any bugs or documentation inconsistencies! <a href="https://github-redirect.dependabot.com/python-attrs/attrs/issues/887">#887</a></p> </li> </ul> <h2>Changes</h2> <ul> <li><code>attr.asdict(retain_collection_types=False)</code> (default) dumps collection-esque keys as tuples. <a href="https://github-redirect.dependabot.com/python-attrs/attrs/issues/646">#646</a>, <a href="https://github-redirect.dependabot.com/python-attrs/attrs/issues/888">#888</a></li> <li><code>__match_args__</code> are now generated to support Python 3.10's <a href="https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching">Structural Pattern Matching</a>. This can be controlled by the <code>match_args</code> argument to the class decorators on Python 3.10 and later. On older versions, it is never added and the argument is ignored. <a href="https://github-redirect.dependabot.com/python-attrs/attrs/issues/815">#815</a></li> <li>If the class-level <em>on_setattr</em> is set to <code>attrs.setters.validate</code> (default in <code>`@define</code>` and <code>`@mutable</code>)` but no field defines a validator, pretend that it's not set. <a href="https://github-redirect.dependabot.com/python-attrs/attrs/issues/817">#817</a></li> <li>The generated <code>__repr__</code> is significantly faster on Pythons with f-strings. <a href="https://github-redirect.dependabot.com/python-attrs/attrs/issues/819">#819</a></li> <li>Attributes transformed via <code>field_transformer</code> are wrapped with <code>AttrsClass</code> again. <a href="https://github-redirect.dependabot.com/python-attrs/attrs/issues/824">#824</a></li> <li>Generated source code is now cached more efficiently for identical classes. <a href="https://github-redirect.dependabot.com/python-attrs/attrs/issues/828">#828</a></li> <li>Added <code>attrs.converters.to_bool()</code>. <a href="https://github-redirect.dependabot.com/python-attrs/attrs/issues/830">#830</a></li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/python-attrs/attrs/blob/main/CHANGELOG.rst">attrs's changelog</a>.</em></p> <blockquote> <h2>21.3.0 (2021-12-28)</h2> <p>Backward-incompatible Changes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</p> <ul> <li> <p>When using <code>`@define</code>,` converters are now run by default when setting an attribute on an instance -- additionally to validators. I.e. the new default is <code>on_setattr=[attrs.setters.convert, attrs.setters.validate]</code>.</p> <p>This is unfortunately a breaking change, but it was an oversight, impossible to raise a <code>DeprecationWarning</code> about, and it's better to fix it now while the APIs are very fresh with few users. <code>[#835](python-attrs/attrs#835) <https://github.com/python-attrs/attrs/issues/835></code><em>, <code>[#886](python-attrs/attrs#886) <https://github.com/python-attrs/attrs/issues/886></code></em></p> </li> <li> <p><code>import attrs</code> has finally landed! As of this release, you can finally import <code>attrs</code> using its proper name.</p> <p>Not all names from the <code>attr</code> namespace have been transferred; most notably <code>attr.s</code> and <code>attr.ib</code> are missing. See <code>attrs.define</code> and <code>attrs.field</code> if you haven't seen our next-generation APIs yet. A more elaborate explanation can be found <code>On The Core API Names <https://www.attrs.org/en/latest/names.html></code>_</p> <p>This feature is at least for one release <strong>provisional</strong>. We don't <em>plan</em> on changing anything, but such a big change is unlikely to go perfectly on the first strike.</p> <p>The API docs have been mostly updated, but it will be an ongoing effort to change everything to the new APIs. Please note that we have <strong>not</strong> moved -- or even removed -- anything from <code>attr</code>!</p> <p>Please do report any bugs or documentation inconsistencies! <code>[#887](python-attrs/attrs#887) <https://github.com/python-attrs/attrs/issues/887></code>_</p> </li> </ul> <p>Changes ^^^^^^^</p> <ul> <li><code>attr.asdict(retain_collection_types=False)</code> (default) dumps collection-esque keys as tuples. <code>[#646](python-attrs/attrs#646) <https://github.com/python-attrs/attrs/issues/646></code><em>, <code>[#888](python-attrs/attrs#888) <https://github.com/python-attrs/attrs/issues/888></code></em></li> <li><code>__match_args__</code> are now generated to support Python 3.10's <code>Structural Pattern Matching <https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching></code><em>. This can be controlled by the <code>match_args</code> argument to the class decorators on Python 3.10 and later. On older versions, it is never added and the argument is ignored. <code>[#815](python-attrs/attrs#815) <https://github.com/python-attrs/attrs/issues/815></code></em></li> <li>If the class-level <em>on_setattr</em> is set to <code>attrs.setters.validate</code> (default in <code>`@define</code>` and <code>`@mutable</code>)` but no field defines a validator, pretend that it's not set. <code>[#817](python-attrs/attrs#817) <https://github.com/python-attrs/attrs/issues/817></code>_</li> <li>The generated <code>__repr__</code> is significantly faster on Pythons with f-strings. <code>[#819](python-attrs/attrs#819) <https://github.com/python-attrs/attrs/issues/819></code>_</li> <li>Attributes transformed via <code>field_transformer</code> are wrapped with <code>AttrsClass</code> again. <code>[#824](python-attrs/attrs#824) <https://github.com/python-attrs/attrs/issues/824></code>_</li> <li>Generated source code is now cached more efficiently for identical classes. <code>[#828](python-attrs/attrs#828) <https://github.com/python-attrs/attrs/issues/828></code>_</li> <li>Added <code>attrs.converters.to_bool()</code>. <code>[#830](python-attrs/attrs#830) <https://github.com/python-attrs/attrs/issues/830></code>_</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/python-attrs/attrs/commit/dd26edd68e12879f716c6554f25d957af299b801"><code>dd26edd</code></a> Prepare 21.3.0</li> <li><a href="https://github.com/python-attrs/attrs/commit/20bf4b6e54a75201b378cff8e6dd9521d2da28f1"><code>20bf4b6</code></a> Go over CONTRIBUTING.md</li> <li><a href="https://github.com/python-attrs/attrs/commit/d528dd425980eff3f43b0e29b0ce4dc81ecd8d84"><code>d528dd4</code></a> Fix more links</li> <li><a href="https://github.com/python-attrs/attrs/commit/fcfb5a692cc8c9f8fde8e39bbd2c5733a47fb1e7"><code>fcfb5a6</code></a> Last pass over changelogs</li> <li><a href="https://github.com/python-attrs/attrs/commit/e09873485e14e9b11d5d590a55280894df367d92"><code>e098734</code></a> Add logo to PyPI description</li> <li><a href="https://github.com/python-attrs/attrs/commit/26c0cef8e48bd131d062d45bdaa0c949d4a2d035"><code>26c0cef</code></a> Streamline workflow</li> <li><a href="https://github.com/python-attrs/attrs/commit/3333e749781a107c829717f7bc0382d33b538b6e"><code>3333e74</code></a> Remove dead achor</li> <li><a href="https://github.com/python-attrs/attrs/commit/2d77d83d4e3ceadbf4414da5963623a20564c415"><code>2d77d83</code></a> Fix dataclass_transform links</li> <li><a href="https://github.com/python-attrs/attrs/commit/b4dc9b07c70c16848960da077fc7ac18fe5e9bc8"><code>b4dc9b0</code></a> Better 2.7 example</li> <li><a href="https://github.com/python-attrs/attrs/commit/d4e32209dc5855796e57c2b08bdc1c1702d051ab"><code>d4e3220</code></a> Use attrs namespace throughout examples.rst</li> <li>Additional commits viewable in <a href="https://github.com/python-attrs/attrs/compare/21.2.0...21.3.0">compare view</a></li> </ul> </details> <br /> [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=attrs&package-manager=pip&previous-version=21.2.0&new-version=21.3.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) You can trigger a rebase of this PR by commenting ``@dependabot` rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - ``@dependabot` rebase` will rebase this PR - ``@dependabot` recreate` will recreate this PR, overwriting any edits that have been made to it - ``@dependabot` merge` will merge this PR after your CI passes on it - ``@dependabot` squash and merge` will squash and merge this PR after your CI passes on it - ``@dependabot` cancel merge` will cancel a previously requested merge and block automerging - ``@dependabot` reopen` will reopen this PR if it is closed - ``@dependabot` close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - ``@dependabot` ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - ``@dependabot` ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - ``@dependabot` ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@jenshnielsen @WilliamHPNielsen @layeoh